shithub: opus

Download patch

ref: 40b309d92bf735af174e44e657c749bd6b5e92ba
parent: e695355ba5ee48eaab0c5c0f65879ac412210add
author: Jean-Marc Valin <jmvalin@jmvalin.ca>
date: Mon Dec 28 21:35:29 EST 2020

WIP: 8-bit SIMD for GRU B

--- a/dnn/dump_data.c
+++ b/dnn/dump_data.c
@@ -31,6 +31,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <stdio.h>
+#include <unistd.h>
 #include "kiss_fft.h"
 #include "common.h"
 #include <math.h>
@@ -141,6 +142,7 @@
   int encode = 0;
   int decode = 0;
   int quantize = 0;
+  srand(getpid());
   st = lpcnet_encoder_create();
   if (argc == 5 && strcmp(argv[1], "-train")==0) training = 1;
   if (argc == 5 && strcmp(argv[1], "-qtrain")==0) {
--- a/dnn/nnet.c
+++ b/dnn/nnet.c
@@ -140,6 +140,7 @@
    compute_activation(output, output, N, layer->activation);
 }
 
+#if 0
 void compute_gru(const GRULayer *gru, float *state, const float *input)
 {
    int i;
@@ -201,6 +202,7 @@
    for (i=0;i<N;i++)
       state[i] = h[i];
 }
+#endif
 
 void compute_gru2(const GRULayer *gru, float *state, const float *input)
 {
@@ -224,7 +226,11 @@
    /* Compute update gate. */
    for (i=0;i<3*N;i++)
       zrh[i] = gru->bias[i];
+#if 1
+   sgemv_accum8x4(zrh, gru->input_weights, 3*N, M, stride, input);
+#else
    sgemv_accum(zrh, gru->input_weights, 3*N, M, stride, input);
+#endif
    for (i=0;i<3*N;i++)
       recur[i] = gru->bias[3*N + i];
    sgemv_accum(recur, gru->recurrent_weights, 3*N, N, stride, state);
--- a/dnn/nnet.h
+++ b/dnn/nnet.h
@@ -56,7 +56,7 @@
 
 typedef struct {
   const float *bias;
-  const float *input_weights;
+  const qweight *input_weights;
   const float *recurrent_weights;
   int nb_inputs;
   int nb_neurons;
--- a/dnn/training_tf2/dump_lpcnet.py
+++ b/dnn/training_tf2/dump_lpcnet.py
@@ -39,7 +39,10 @@
 max_conv_inputs = 1
 max_mdense_tmp = 1
 
-def printVector(f, vector, name, dtype='float'):
+def printVector(f, vector, name, dtype='float', dotp=False):
+    if dotp:
+        vector = vector.reshape((vector.shape[0]//4, 4, vector.shape[1]//8, 8))
+        vector = vector.transpose((2, 0, 3, 1))
     v = np.reshape(vector, (-1));
     #print('static const float ', name, '[', len(v), '] = \n', file=f)
     f.write('static const {} {}[{}] = {{\n   '.format(dtype, name, len(v)))
@@ -127,7 +130,12 @@
     name = self.name
     print("printing layer " + name + " of type " + self.__class__.__name__)
     weights = self.get_weights()
+    f.write('#ifdef DOT_PROD\n')
+    qweight = np.clip((128*weights[0]).astype('int'), -128, 127)
+    printVector(f, qweight, name + '_weights', dotp=True, dtype='qweight')
+    f.write('#else /*DOT_PROD*/\n')
     printVector(f, weights[0], name + '_weights')
+    f.write('#endif /*DOT_PROD*/\n')
     printVector(f, weights[1], name + '_recurrent_weights')
     printVector(f, weights[-1], name + '_bias')
     if hasattr(self, 'activation'):
--- a/dnn/vec.h
+++ b/dnn/vec.h
@@ -41,10 +41,11 @@
 #include "vec_neon.h"
 #else
 
+#define MAX_INPUTS (2048)
 
 #define NO_OPTIMIZATIONS
 
-//#define DOT_PROD
+#define DOT_PROD
 //#define USE_SU_BIAS
 
 #ifdef DOT_PROD
@@ -193,12 +194,46 @@
 }
 
 #ifdef DOT_PROD
+#define SCALE (128.f*127.f)
+#define SCALE_1 (1.f/128.f/127.f)
 
-#define MAX_INPUTS (2048)
+static inline void sgemv_accum8x4(float *out, const qweight *w, int rows, int cols, int col_stride, const float *_x)
+{
+   int i, j;
+   signed char x[MAX_INPUTS];
+   (void)col_stride;
+   for (i=0;i<rows;i++) out[i] *= SCALE;
+   for (i=0;i<cols;i++) x[i] = (int)floor(.5+127*_x[i]);
+   for (i=0;i<rows;i+=8)
+   {
+      for (j=0;j<cols;j+=4)
+      {
+         float * restrict y;
+         float xj0, xj1, xj2, xj3;
+         xj0 = x[j+0];
+         xj1 = x[j+1];
+         xj2 = x[j+2];
+         xj3 = x[j+3];
+         y = &out[i];
+         y[0] += (w[0]*xj0+w[1]*xj1+w[2]*xj2+w[3]*xj3);
+         y[1] += (w[4]*xj0+w[5]*xj1+w[6]*xj2+w[7]*xj3);
+         y[2] += (w[8]*xj0+w[9]*xj1+w[10]*xj2+w[11]*xj3);
+         y[3] += (w[12]*xj0+w[13]*xj1+w[14]*xj2+w[15]*xj3);
+         y[4] += (w[16]*xj0+w[17]*xj1+w[18]*xj2+w[19]*xj3);
+         y[5] += (w[20]*xj0+w[21]*xj1+w[22]*xj2+w[23]*xj3);
+         y[6] += (w[24]*xj0+w[25]*xj1+w[26]*xj2+w[27]*xj3);
+         y[7] += (w[28]*xj0+w[29]*xj1+w[30]*xj2+w[31]*xj3);
+         w += 32;
+      }
+   }
+   for (i=0;i<rows;i++) out[i] *= SCALE_1;
+}
+#else
+#define sgemv_accum sgemv_accum8x4
+#endif
 
+#ifdef DOT_PROD
 
-#define SCALE (128.f*127.f)
-#define SCALE_1 (1.f/128.f/127.f)
 
 #ifdef USE_SU_BIAS
 static inline void sparse_sgemv_accum8x4(float *out, const qweight *w, int rows, int cols, const int *idx, const float *_x)
--