tensorflow/lite/kernels/internal/optimized/neon_tensor_utils.cc
// Note: this function caps minimum and maximum at zero, unlike the true
// minmax_element. This is intentional.
this function caps minimum and maximum at zero, unlike the true
minmax_element. This is intentional.
this function caps minimum and maximum at zero unlike the true minmax_element . This is intentional .
inline void NeonMinMax(const float* values, const int size, float* min,
                       float* max) {
  const int postamble_start = RoundDownVectors<kFloatValuesPerNeonVector>(size);
  float rmin = 0.0f, rmax = 0.0f;
  int i = 0;
  if (postamble_start) {
    float32x4_t min_f32x4 = vld1q_f32(values);
    float32x4_t max_f32x4 = min_f32x4;
    for (i = kFloatValuesPerNeonVector; i < postamble_start;
         i += kFloatValuesPerNeonVector) {
      const float32x4_t value0_f32x4 = vld1q_f32(&values[i]);
      min_f32x4 = vminq_f32(min_f32x4, value0_f32x4);
      max_f32x4 = vmaxq_f32(max_f32x4, value0_f32x4);
    }
#ifdef __aarch64__
    rmin = std::min(rmin, vminvq_f32(min_f32x4));
    rmax = std::max(rmax, vmaxvq_f32(max_f32x4));
#else
    float32x2_t min_f32x2 =
        vmin_f32(vget_low_f32(min_f32x4), vget_high_f32(min_f32x4));
    float32x2_t max_f32x2 =
        vmax_f32(vget_low_f32(max_f32x4), vget_high_f32(max_f32x4));
    min_f32x2 = vpmin_f32(min_f32x2, min_f32x2);
    max_f32x2 = vpmax_f32(max_f32x2, max_f32x2);
    rmin = std::min(rmin, vget_lane_f32(min_f32x2, 0));
    rmax = std::max(rmax, vget_lane_f32(max_f32x2, 0));
#endif  
  }
  if (TFLITE_UNLIKELY(i < size)) {
    const auto minmax =
        std::minmax_element(values + postamble_start, values + size);
    rmin = std::min(rmin, *minmax.first);
    rmax = std::max(rmax, *minmax.second);
  }
  *min = rmin;
  *max = rmax;
}
inline void NeonMinMax ( const float * values , const int size , float * min , float * max ) { const int postamble_start = RoundDownVectors < kFloatValuesPerNeonVector > ( size ) ; float rmin = 0.0f , rmax = 0.0f ; int i = 0 ; if ( postamble_start ) { float32x4_t min_f32x4 = vld1q_f32 ( values ) ; float32x4_t max_f32x4 = min_f32x4 ; for ( i = kFloatValuesPerNeonVector ; i < postamble_start ; i += kFloatValuesPerNeonVector ) { const float32x4_t value0_f32x4 = vld1q_f32 ( & values [ i ] ) ; min_f32x4 = vminq_f32 ( min_f32x4 , value0_f32x4 ) ; max_f32x4 = vmaxq_f32 ( max_f32x4 , value0_f32x4 ) ; } #ifdef __aarch64__ rmin = std :: min ( rmin , vminvq_f32 ( min_f32x4 ) ) ; rmax = std :: max ( rmax , vmaxvq_f32 ( max_f32x4 ) ) ; #else float32x2_t min_f32x2 = vmin_f32 ( vget_low_f32 ( min_f32x4 ) , vget_high_f32 ( min_f32x4 ) ) ; float32x2_t max_f32x2 = vmax_f32 ( vget_low_f32 ( max_f32x4 ) , vget_high_f32 ( max_f32x4 ) ) ; min_f32x2 = vpmin_f32 ( min_f32x2 , min_f32x2 ) ; max_f32x2 = vpmax_f32 ( max_f32x2 , max_f32x2 ) ; rmin = std :: min ( rmin , vget_lane_f32 ( min_f32x2 , 0 ) ) ; rmax = std :: max ( rmax , vget_lane_f32 ( max_f32x2 , 0 ) ) ; #endif } if ( TFLITE_UNLIKELY ( i < size ) ) { const auto minmax = std :: minmax_element ( values + postamble_start , values + size ) ; rmin = std :: min ( rmin , * minmax . first ) ; rmax = std :: max ( rmax , * minmax . second ) ; } * min = rmin ; * max = rmax ; }
Note: this function caps minimum and maximum at zero, unlike the true
minmax_element.
Note : this function caps minimum and maximum at zero unlike the true minmax_element .
// __aarch64__