Repository: tesseract-ocr/tesseract
File: src/arch/intsimdmatrixneon.cpp (C++)

Code before:
    *v++ = (static_cast<double>(vget_lane_s32(vget_high_s32(result4567), 0)) / INT8_MAX + *wi++) * *scales++;
  if (num_out > 7)
    *v   = (static_cast<double>(vget_lane_s32(vget_high_s32(result4567), 1)) / INT8_MAX + *wi  ) * *scales;
}

static void matrixDotVector(int dim1, int dim2, const int8_t* wi,
                            const double* scales, const int8_t* u, double* v) {
  const int num_out = dim1;
  const int num_in = dim2 - 1;
  // Each call to a partial_func_ produces group_size outputs, except the
  // last one, which can produce less.
  const int rounded_num_in =
    IntSimdMatrix::Roundup(num_in, kNumInputsPerGroup);
  int group_size = kNumOutputsPerRegister * kMaxOutputRegisters;
  int output = 0;

  int w_step = (rounded_num_in + 1) * group_size;

  for (; output + group_size <= num_out; output += group_size) {
    PartialMatrixDotVector8(wi, scales, u, rounded_num_in, v, 8);
    wi += w_step;
    scales += group_size;
    v += group_size;
  }
  if (output < num_out)
    PartialMatrixDotVector8(wi, scales, u, rounded_num_in, v, num_out & 7);
}

const IntSimdMatrix IntSimdMatrix::intSimdMatrixNEON = {
  // Function.
  matrixDotVector,
  // Number of 32 bit outputs held in each register.
  kNumOutputsPerRegister,
  // Maximum number of registers that we will use to hold outputs.
  kMaxOutputRegisters,
  // Number of 8 bit inputs in the inputs register.
  kNumInputsPerRegister,
  // Number of inputs in each weight group.
  kNumInputsPerGroup
};

}  // namespace tesseract.

#endif /* __ARM_NEON */


Review comment (suggestion):
```suggestion
    PartialMatrixDotVector8(wi, scales, u, rounded_num_in, v, num_out % kNumOutputsPerRegister);
```

Code after:
    *v++ = (static_cast<double>(vget_lane_s32(vget_high_s32(result4567), 0)) / INT8_MAX + *wi++) * *scales++;
  if (num_out > 7)
    *v   = (static_cast<double>(vget_lane_s32(vget_high_s32(result4567), 1)) / INT8_MAX + *wi  ) * *scales;
}

static void matrixDotVector(int dim1, int dim2, const int8_t* wi,
                            const double* scales, const int8_t* u, double* v) {
  const int num_out = dim1;
  const int num_in = dim2 - 1;
  // Each call to a partial_func_ produces group_size outputs, except the
  // last one, which can produce less.
  const int rounded_num_in =
    IntSimdMatrix::Roundup(num_in, kNumInputsPerGroup);
  int group_size = kNumOutputsPerRegister * kMaxOutputRegisters;
  int output = 0;

  int w_step = (rounded_num_in + 1) * group_size;

  for (; output + group_size <= num_out; output += group_size) {
    PartialMatrixDotVector8(wi, scales, u, rounded_num_in, v, kNumOutputsPerRegister);
    wi += w_step;
    scales += group_size;
    v += group_size;
  }
  if (output < num_out)
    PartialMatrixDotVector8(wi, scales, u, rounded_num_in, v, num_out & (kNumOutputsPerRegister-1));
}

const IntSimdMatrix IntSimdMatrix::intSimdMatrixNEON = {
  // Function.
  matrixDotVector,
  // Number of 32 bit outputs held in each register.
  kNumOutputsPerRegister,
  // Maximum number of registers that we will use to hold outputs.
  kMaxOutputRegisters,
  // Number of 8 bit inputs in the inputs register.
  kNumInputsPerRegister,
  // Number of inputs in each weight group.
  kNumInputsPerGroup
};

}  // namespace tesseract.

#endif /* __ARM_NEON */