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

Code before:
///////////////////////////////////////////////////////////////////////

#if !defined(__ARM_NEON)
//#error Implementation only for NEON capable architectures
#else

#include "intsimdmatrix.h"

#include "arm_neon.h"
#include <cstdint>
#include <algorithm>
#include <vector>

namespace tesseract {

// Number of outputs held in each register. (Actually, we use a
// pair of 4x32 registers, so 8 x 32 bit ints).
constexpr int kNumOutputsPerRegister = 8;
// Maximum number of registers that we will use.
constexpr int kMaxOutputRegisters = 1;
// Number of inputs in the inputs register.
constexpr int kNumInputsPerRegister = 8;//32;
// Number of inputs in each weight group.
constexpr int kNumInputsPerGroup = 8;

// Functions to compute part of a matrix.vector multiplication. The weights
// are in a very specific order (see above) in w, which is multiplied by
// u of length num_in, to produce output v after scaling the integer results
// by the corresponding member of scales.
// The amount of w and scales consumed is fixed and not available to the
// caller. The number of outputs written to v will be at most num_out.

// Computes part of matrix.vector v = Wu. Computes N=8 results.
// The weights *must* be arranged so that consecutive reads from wi
// provides (num_in/kNumInputsPerGroup groups of (N output dim groups of
// (kNumInputsPerGroup inputs))). After that there must be N consecutive
// bias weights, before continuing with any more weights.
// u must be padded out with zeros to
// kNumInputsPerGroup*ceil(num_in/kNumInputsPerGroup) elements.
static void PartialMatrixDotVector8(const int8_t* __restrict wi, const double* __restrict scales,
                                     const int8_t* __restrict u, int num_in, int num_out,
                                     double* __restrict v) {
  // Initialize all the results to 0.
  int32x4_t result0123 = { 0, 0, 0, 0 };
  int32x4_t result4567 = { 0, 0, 0, 0 };
  // Iterate over the input (u), one registerful at a time.
  for (int j = 0; j < num_in; j += 8) {
    int8x8_t  vu   = vld1_s8(u);       // vu     = u0  u1  u2  u3  u4  u5  u6  u7
    int8x16_t vw01 = vld1q_s8(wi);     // vw0    = w00 w01 w02 w03 w04 w05 w06 w07 w10 w11 w12 w13 w14 w15 w16 w17
    int8x16_t vw23 = vld1q_s8(wi+8*2); // vw2    = w20 w21 w22 w23 w24 w25 w26 w27 w30 w31 w32 w33 w34 w35 w36 w37
    int8x16_t vw45 = vld1q_s8(wi+8*4); // vw4    = w40 w41 w42 w43 w44 w45 w46 w47 w50 w51 w52 w53 w54 w55 w56 w57

Review comment (suggestion):
```suggestion
// Function to compute part of a matrix.vector multiplication. The weights
```

Code after:

#if !defined(__ARM_NEON)
//#error Implementation only for NEON capable architectures
#else

#include "intsimdmatrix.h"

#include "arm_neon.h"
#include <cstdint>
#include <algorithm>
#include <vector>

namespace tesseract {

// Number of outputs held in each register. (Actually, we use a
// pair of 4x32 registers, so 8 x 32 bit ints).
constexpr int kNumOutputsPerRegister = 8;
// Maximum number of registers that we will use.
constexpr int kMaxOutputRegisters = 1;
// Number of inputs in the inputs register.
constexpr int kNumInputsPerRegister = 8;
// Number of inputs in each weight group.
constexpr int kNumInputsPerGroup = 8;

// Function to compute part of a matrix.vector multiplication. The weights
// are in a very specific order (see above) in w, which is multiplied by
// u of length num_in, to produce output v after scaling the integer results
// by the corresponding member of scales.
// The amount of w and scales consumed is fixed and not available to the
// caller.

// Computes part of matrix.vector v = Wu. Computes N=8 results.
// The weights *must* be arranged so that consecutive reads from wi
// provides (num_in/kNumInputsPerGroup groups of (N output dim groups of
// (kNumInputsPerGroup inputs))). After that there must be N consecutive
// bias weights, before continuing with any more weights.
// u must be padded out with zeros to
// kNumInputsPerGroup*ceil(num_in/kNumInputsPerGroup) elements.
static void PartialMatrixDotVector8(const int8_t* __restrict wi, const double* __restrict scales,
                                     const int8_t* __restrict u, int num_in,
                                     double* __restrict v) {
  // Initialize all the results to 0.
  int32x4_t result0123 = { 0, 0, 0, 0 };
  int32x4_t result4567 = { 0, 0, 0, 0 };
  // Iterate over the input (u), one registerful at a time.
  for (int j = 0; j < num_in; j += 8) {
    int8x8_t  vu   = vld1_s8(u);       // vu     = u0  u1  u2  u3  u4  u5  u6  u7
    int8x16_t vw01 = vld1q_s8(wi);     // vw0    = w00 w01 w02 w03 w04 w05 w06 w07 w10 w11 w12 w13 w14 w15 w16 w17
    int8x16_t vw23 = vld1q_s8(wi+8*2); // vw2    = w20 w21 w22 w23 w24 w25 w26 w27 w30 w31 w32 w33 w34 w35 w36 w37
    int8x16_t vw45 = vld1q_s8(wi+8*4); // vw4    = w40 w41 w42 w43 w44 w45 w46 w47 w50 w51 w52 w53 w54 w55 w56 w57
    int8x16_t vw67 = vld1q_s8(wi+8*6); // vw6    = w60 w61 w62 w63 w64 w65 w66 w67 w70 w71 w72 w73 w74 w75 w76 w77