| 173 | }; |
| 174 | |
| 175 | inline void Mean(const tflite::MeanParams& op_params, |
| 176 | const RuntimeShape& unextended_input_shape, |
| 177 | const int8_t* input_data, int32 input_zero_point, |
| 178 | float input_scale, const RuntimeShape& unextended_output_shape, |
| 179 | int8_t* output_data, int32 output_zero_point, |
| 180 | float output_scale, CpuBackendContext* cpu_backend_context) { |
| 181 | gemmlowp::ScopedProfilingLabel label("Mean4D/Int8"); |
| 182 | // Current implementation only supports dimension equals 4 and simultaneous |
| 183 | // reduction over width and height. |
| 184 | TFLITE_CHECK_EQ(unextended_input_shape.DimensionsCount(), 4); |
| 185 | TFLITE_CHECK_LE(unextended_output_shape.DimensionsCount(), 4); |
| 186 | const RuntimeShape input_shape = |
| 187 | RuntimeShape::ExtendedShape(4, unextended_input_shape); |
| 188 | const RuntimeShape output_shape = |
| 189 | RuntimeShape::ExtendedShape(4, unextended_output_shape); |
| 190 | const int output_height = output_shape.Dims(1); |
| 191 | const int output_width = output_shape.Dims(2); |
| 192 | const int output_depth = output_shape.Dims(3); |
| 193 | |
| 194 | TFLITE_DCHECK_EQ(op_params.axis_count, 2); |
| 195 | TFLITE_DCHECK((op_params.axis[0] == 1 && op_params.axis[1] == 2) || |
| 196 | (op_params.axis[0] == 2 && op_params.axis[1] == 1)); |
| 197 | TFLITE_DCHECK_EQ(output_height, 1); |
| 198 | TFLITE_DCHECK_EQ(output_width, 1); |
| 199 | |
| 200 | constexpr int kMinDepthPerThread = 8; |
| 201 | int thread_count = output_depth / kMinDepthPerThread; |
| 202 | thread_count = thread_count > 0 ? thread_count : 1; |
| 203 | const int capped_thread_count = |
| 204 | std::min(thread_count, cpu_backend_context->max_num_threads()); |
| 205 | |
| 206 | if (capped_thread_count == 1) { |
| 207 | MeanImpl(op_params, input_shape, input_data, input_zero_point, input_scale, |
| 208 | output_shape, output_data, output_zero_point, output_scale, 0, |
| 209 | output_depth); |
| 210 | } else { |
| 211 | // Instead parrallel for batch, we loop for the output_depth since batch |
| 212 | // is typical 1. |
| 213 | std::vector<MeanWorkerTask> tasks; |
| 214 | // TODO(b/131746020) don't create new heap allocations every time. |
| 215 | // At least we make it a single heap allocation by using reserve(). |
| 216 | tasks.reserve(capped_thread_count); |
| 217 | int depth_start = 0; |
| 218 | for (int i = 0; i < capped_thread_count; ++i) { |
| 219 | // Try to distribute the tasks as even as possible. |
| 220 | int depth_end = depth_start + |
| 221 | (output_depth - depth_start) / (capped_thread_count - i); |
| 222 | tasks.emplace_back(op_params, input_shape, input_data, input_zero_point, |
| 223 | input_scale, output_shape, output_data, |
| 224 | output_zero_point, output_scale, depth_start, |
| 225 | depth_end); |
| 226 | depth_start = depth_end; |
| 227 | } |
| 228 | cpu_backend_threadpool::Execute(tasks.size(), tasks.data(), |
| 229 | cpu_backend_context); |
| 230 | } |
| 231 | } |
| 232 |
nothing calls this directly
no test coverage detected