| 847 | #endif // USE_NEON |
| 848 | |
| 849 | inline void MeanImpl(const tflite::MeanParams& op_params, |
| 850 | const RuntimeShape& input_shape, const uint8_t* input_data, |
| 851 | int32 input_zero_point, float input_scale, |
| 852 | const RuntimeShape& output_shape, uint8_t* output_data, |
| 853 | int32 output_zero_point, float output_scale, |
| 854 | int start_depth, int end_depth) { |
| 855 | gemmlowp::ScopedProfilingLabel label("Mean4D/Uint8/MeanImpl"); |
| 856 | |
| 857 | // Current implementation only supports dimension equals 4 and simultaneous |
| 858 | // reduction over width and height. |
| 859 | const int output_batch = output_shape.Dims(0); |
| 860 | const int output_height = output_shape.Dims(2); |
| 861 | const int output_width = output_shape.Dims(2); |
| 862 | const int input_height = input_shape.Dims(1); |
| 863 | const int input_width = input_shape.Dims(2); |
| 864 | const float num_elements_in_axis = input_width * input_height; |
| 865 | |
| 866 | TFLITE_DCHECK_EQ(op_params.axis_count, 2); |
| 867 | TFLITE_DCHECK((op_params.axis[0] == 1 && op_params.axis[1] == 2) || |
| 868 | (op_params.axis[0] == 2 && op_params.axis[1] == 1)); |
| 869 | TFLITE_DCHECK_EQ(output_height, 1); |
| 870 | TFLITE_DCHECK_EQ(output_width, 1); |
| 871 | |
| 872 | const bool ordinary_mean = |
| 873 | (input_zero_point == output_zero_point && input_scale == output_scale); |
| 874 | float scale = 0.0f, bias = 0.0f; |
| 875 | if (!ordinary_mean) { |
| 876 | scale = input_scale / output_scale; |
| 877 | bias = -input_zero_point * scale + 0.5; |
| 878 | } |
| 879 | |
| 880 | #ifdef USE_NEON |
| 881 | const float32x4_t num_elements_dup = vdupq_n_f32(num_elements_in_axis); |
| 882 | // This is only an approximation as NEON does not offer division instruction. |
| 883 | const float32x4_t scale_dup = vdupq_n_f32(scale); |
| 884 | const float32x4_t num_elements_reverse = vrecpeq_f32(num_elements_dup); |
| 885 | float32x4_t zero_point_with_bias_dup = vdupq_n_f32(output_zero_point + bias); |
| 886 | #endif // USE_NEON |
| 887 | |
| 888 | for (int out_b = 0; out_b < output_batch; ++out_b) { |
| 889 | int out_d = start_depth; |
| 890 | #ifdef USE_NEON |
| 891 | |
| 892 | for (; out_d < end_depth - 8; out_d += 8) { |
| 893 | float32x4_t temp_sum_1 = vdupq_n_f32(0); |
| 894 | float32x4_t temp_sum_2 = vdupq_n_f32(0); |
| 895 | for (int in_h = 0; in_h < input_height; ++in_h) { |
| 896 | for (int in_w = 0; in_w < input_width; ++in_w) { |
| 897 | const uint8_t* input_data_ptr = |
| 898 | input_data + Offset(input_shape, out_b, in_h, in_w, out_d); |
| 899 | uint8x8_t input_data_val = vld1_u8(input_data_ptr); |
| 900 | int16x8_t input_data_val_shift = |
| 901 | vreinterpretq_s16_u16(vmovl_u8(input_data_val)); |
| 902 | float32x4_t input_float_1 = |
| 903 | vcvtq_f32_s32(vmovl_s16(vget_high_s16(input_data_val_shift))); |
| 904 | float32x4_t input_float_2 = |
| 905 | vcvtq_f32_s32(vmovl_s16(vget_low_s16(input_data_val_shift))); |
| 906 | temp_sum_1 = vaddq_f32(temp_sum_1, input_float_1); |
no test coverage detected