1082fcbecffc8b7a775f5131859a28daeb54aae7
[platform/adaptation/npu/trix-engine.git] / src / core / ne-data.cc
1 /**
2  * Proprietary
3  * Copyright (C) 2020 Samsung Electronics
4  * Copyright (C) 2020 Dongju Chae <dongju.chae@samsung.com>
5  */
6 /**
7  * @file ne-data.cc
8  * @date 25 Aug 2020
9  * @brief Data format converter for NPU Engine (NE) users.
10  * @author Dongju Chae <dongju.chae@samsung.com>
11  * @bug No known bugs except for NYI items
12  */
13
14 #include <ne-model.h>
15 #include <ne-data.h>
16 #include <ne-utils.h>
17
18 #define TAG _N2
19
20 #define switch_npu_type(S, n)                       \
21   switch (n) {                                      \
22     case DATA_TYPE_SRNPU:                           \
23       /* no-break */                                \
24     case DATA_TYPE_QASYMM8:                         \
25       quantizer = new QuantizerImpl<S, uint8_t>;    \
26       break;                                        \
27     case DATA_TYPE_QSYMM16:                         \
28       quantizer = new QuantizerImpl<S, int16_t>;    \
29       break;                                        \
30     default:                                        \
31       logerr (TAG, "Unsupported datatype %d\n", n); \
32       return 0;                                     \
33   }
34
35 #define switch_std_npu_type(s, n)                   \
36   switch (s) {                                      \
37     case DATA_TYPE_INT8:                            \
38       switch_npu_type (int8_t, n);                  \
39       break;                                        \
40     case DATA_TYPE_UINT8:                           \
41       switch_npu_type (uint8_t, n);                 \
42       break;                                        \
43     case DATA_TYPE_INT16:                           \
44       switch_npu_type (int16_t, n);                 \
45       break;                                        \
46     case DATA_TYPE_UINT16:                          \
47       switch_npu_type (uint16_t, n);                \
48       break;                                        \
49     case DATA_TYPE_INT32:                           \
50       switch_npu_type (int32_t, n);                 \
51       break;                                        \
52     case DATA_TYPE_UINT32:                          \
53       switch_npu_type (uint32_t, n);                \
54       break;                                        \
55     case DATA_TYPE_INT64:                           \
56       switch_npu_type (int64_t, n);                 \
57       break;                                        \
58     case DATA_TYPE_UINT64:                          \
59       switch_npu_type (uint64_t, n);                \
60       break;                                        \
61     case DATA_TYPE_FLOAT32:                         \
62       switch_npu_type (float, n);                   \
63       break;                                        \
64     case DATA_TYPE_FLOAT64:                         \
65       switch_npu_type (double, n);                  \
66       break;                                        \
67     default:                                        \
68       logerr (TAG, "Unsupported datatype %d\n", s); \
69       return 0;                                     \
70   }
71
72 #define DECLARE_QUANTIZER(st, dt)                                    \
73   Quantizer *quantizer = nullptr;                                    \
74   if (get_data_size (st) != get_data_size (dt)) {                    \
75     if (to_npu_)                                                     \
76       switch_std_npu_type (st, dt) else switch_std_npu_type (dt, st) \
77           quantizer->set_direction (to_npu_);                        \
78     quantizer->set_quant (zero_, scale_);                            \
79   }
80
81 /** @brief The base class for quantization */
82 class Quantizer {
83  public:
84   Quantizer () : to_npu_ (true), zero_ (0), scale_ (0.0) {}
85   virtual ~Quantizer () {}
86
87   void set_direction (bool to_npu) { to_npu_ = to_npu; }
88
89   void set_quant (uint32_t zero, float scale) {
90     zero_ = zero;
91     scale_ = scale;
92   }
93
94   virtual void memcpy (void *dst, void *src, size_t size) {}
95
96  protected:
97   bool to_npu_;
98   uint32_t zero_;
99   float scale_;
100 };
101
102 /** @brief The derived class for quantization with various data types */
103 template <typename S, typename N>
104 class QuantizerImpl : public Quantizer {
105  public:
106   QuantizerImpl () {}
107   ~QuantizerImpl () {}
108
109   void memcpy (void *dst, void *src, size_t size);
110
111  private:
112   void quantized_memcpy (void *dst, void *src, uint32_t num);
113   void dequantized_memcpy (void *dst, void *src, uint32_t num);
114 };
115
116 /** @brief memory copy wrapper for quantization */
117 template <typename S, typename N>
118 void
119 QuantizerImpl<S, N>::memcpy (void *dst, void *src, size_t size) {
120   if (to_npu_)
121     quantized_memcpy (dst, src, size / sizeof (S));
122   else
123     dequantized_memcpy (dst, src, size / sizeof (N));
124 }
125
126 /** @brief quantized memory copy */
127 template <typename S, typename N>
128 void
129 QuantizerImpl<S, N>::quantized_memcpy (void *dst, void *src, uint32_t num) {
130   double scale = (double) scale_;
131   double zero = (double) zero_;
132   double min, max, val;
133
134   if (!src || !dst || num == 0 || scale == 0.0) {
135     logerr (TAG, "Invalid parameter detected\n");
136     return;
137   }
138
139   switch (sizeof (N)) {
140     case 1:
141       min = 0.0;
142       max = 255.0;
143       break;
144     case 2:
145       min = -32768.0;
146       max = 32767.0;
147       break;
148     default:
149       logerr (TAG, "Unsupported quantization size: %d\n", sizeof (N));
150       return;
151   }
152
153   uint32_t idx = 0;
154   while (idx < num) {
155     val = ((S *) src)[idx];
156     val = val / scale;
157     val += zero;
158     val = (val > max) ? max : val;
159     val = (val < min) ? min : val;
160     ((N *) dst)[idx++] = (N) val;
161   }
162 }
163
164 /** @brief dequantized memory copy */
165 template <typename S, typename N>
166 void
167 QuantizerImpl<S, N>::dequantized_memcpy (void *dst, void *src, uint32_t num) {
168   double scale = (double) scale_;
169   double zero = (double) zero_;
170   double val;
171
172   if (!src || !dst || num == 0) {
173     logerr (TAG, "Invalid parameter detected\n");
174     return;
175   }
176
177   switch (sizeof (N)) {
178     case 1:
179       break;
180     case 2:
181       zero = 0; /* ignored */
182       break;
183     default:
184       logerr (TAG, "Unsupported quantization size: %d\n", sizeof (N));
185       return;
186   }
187
188   uint32_t idx = 0;
189   while (idx < num) {
190     val = ((N *) src)[idx];
191     val -= zero;
192     val *= scale;
193     ((S *) dst)[idx++] = (S) val;
194   }
195 }
196
197 /**
198  * @brief check data format conversion capability
199  * @return true or false
200  * @note Support NHWC/NCHW as the standard format
201  */
202 bool
203 DataConverter::checkCapability () {
204   /* if not resolved yet */
205   if (src_layout_ == DATA_LAYOUT_MODEL || dst_layout_ == DATA_LAYOUT_MODEL)
206     return false;
207
208   if (src_type_ == DATA_TYPE_MODEL || dst_type_ == DATA_TYPE_MODEL)
209     return false;
210
211   /* same layout */
212   if (src_layout_ == dst_layout_)
213     return true;
214
215   /* raw layout */
216   if (src_layout_ == DATA_LAYOUT_RAW || dst_layout_ == DATA_LAYOUT_RAW)
217     return true;
218
219   /* standard -> trinity */
220   if ((src_layout_ == DATA_LAYOUT_NHWC || src_layout_ == DATA_LAYOUT_NCHW) &&
221       (dst_layout_ == DATA_LAYOUT_TRIV || dst_layout_ == DATA_LAYOUT_TRIV2))
222     return true;
223
224   /* trinity -> standard */
225   if ((src_layout_ == DATA_LAYOUT_TRIV || src_layout_ == DATA_LAYOUT_TRIV2) &&
226       (dst_layout_ == DATA_LAYOUT_NHWC || dst_layout_ == DATA_LAYOUT_NCHW))
227     return true;
228
229   return false;
230 }
231
232 /**
233  * @brief check layout conversion is required
234  * @return true if required. otherwise false
235  */
236 bool
237 DataConverter::needLayoutConversion () {
238   /* don't care about DATA_LAYOUT_RAW */
239   if (to_npu_ && dst_layout_ == DATA_LAYOUT_RAW)
240     return false;
241
242   if (!to_npu_ && src_layout_ == DATA_LAYOUT_RAW)
243     return false;
244
245   return src_layout_ != dst_layout_;
246 }
247
248 /**
249  * @brief convert data format, copying the converted source data to destination.
250  * @return the number of bytes copied
251  */
252 size_t
253 DataConverter::perform () {
254   /* check parameters first */
255   if (!src_ || !dst_ || size_ == 0 || !dims_) {
256     logerr (TAG, "Invalid parameter detected");
257     return 0;
258   }
259
260   if (!checkCapability ()) {
261     logerr (TAG, "Unable to pass capability check");
262     return 0;
263   }
264
265   DECLARE_QUANTIZER (src_type_, dst_type_);
266
267   if (needLayoutConversion ()) {
268     /* trinity device assumes the NHWC-based layout */
269     uint32_t batch = dims_[0];
270     uint32_t height = dims_[1];
271     uint32_t width = dims_[2];
272     uint32_t depth = dims_[3];
273
274     uint32_t src_data_size = get_data_size (src_type_);
275     uint32_t dst_data_size = get_data_size (dst_type_);
276     uint32_t npu_data_size;
277     if (to_npu_)
278       npu_data_size = dst_data_size;
279     else
280       npu_data_size = src_data_size;
281
282     uint32_t granularity = DATA_GRANULARITY;
283     uint32_t granularity_div = 1;
284
285     if (npu_tops_ == 2)
286       granularity_div *= 2;
287     if (npu_data_size == 2)
288       granularity_div *= 2;
289
290     granularity /= granularity_div;
291
292     uint32_t MPA_L = granularity;
293     uint32_t src_offset;
294     uint32_t dst_offset;
295     uint32_t slice_size;
296
297     if (src_layout_ == DATA_LAYOUT_NHWC && dst_layout_ == DATA_LAYOUT_TRIV2) {
298       /* special handling: input image */
299       if (depth == 1 || depth == 3)
300         goto try_quantize;
301       /* special handling: depth == granularity */
302       if (depth == granularity)
303         goto try_quantize;
304       /* NHWC --> TRIV2 */
305       for (uint32_t n = 0; n < batch; n++) {
306         for (uint32_t h = 0; h < height; h++) {
307           for (uint32_t w = 0; w < width; w++) {
308             for (uint32_t d = 0; d < depth; d += MPA_L) {
309               src_offset = d + depth * (w + width * (h + n * height));
310               dst_offset = MPA_L * (w + width * (h + (n + d / MPA_L) * height));
311
312               src_offset *= src_data_size;
313               dst_offset *= dst_data_size;
314
315               slice_size = (depth - d >= MPA_L) ? MPA_L : depth - d;
316               slice_size *= src_data_size;
317
318               if (quantizer)
319                 quantizer->memcpy (static_cast<char *> (dst_) + dst_offset,
320                                    static_cast<char *> (src_) + src_offset,
321                                    slice_size);
322               else
323                 memcpy (static_cast<char *> (dst_) + dst_offset,
324                         static_cast<char *> (src_) + src_offset, slice_size);
325             }
326           }
327         }
328       }
329     } else if (src_layout_ == DATA_LAYOUT_NCHW &&
330                dst_layout_ == DATA_LAYOUT_TRIV2) {
331       /* special handling: NHWC == NCHW */
332       if (depth == 1)
333         goto try_quantize;
334       if (depth == 3)
335         MPA_L = 3;
336       /* NCHW --> TRIV2 */
337       slice_size = src_data_size;
338       for (uint32_t n = 0; n < batch; n++) {
339         for (uint32_t d = 0; d < depth; d++) {
340           for (uint32_t h = 0; h < height; h++) {
341             for (uint32_t w = 0; w < width; w++) {
342               src_offset = w + width * (h + height * (d + n * depth));
343               dst_offset = (d % MPA_L) +
344                            MPA_L * (w + width * (h + (n + d / MPA_L) * height));
345
346               src_offset *= src_data_size;
347               dst_offset *= dst_data_size;
348
349               if (quantizer)
350                 quantizer->memcpy (static_cast<char *> (dst_) + dst_offset,
351                                    static_cast<char *> (src_) + src_offset,
352                                    slice_size);
353               else
354                 memcpy (static_cast<char *> (dst_) + dst_offset,
355                         static_cast<char *> (src_) + src_offset, slice_size);
356             }
357           }
358         }
359       }
360     } else if (src_layout_ == DATA_LAYOUT_TRIV2 &&
361                dst_layout_ == DATA_LAYOUT_NHWC) {
362       /* special handling: depth == granularity */
363       if (depth == granularity)
364         goto try_quantize;
365       /* TRIV2 --> NHWC */
366       for (uint32_t n = 0; n < batch; n++) {
367         for (uint32_t h = 0; h < height; h++) {
368           for (uint32_t w = 0; w < width; w++) {
369             for (uint32_t d = 0; d < depth; d += MPA_L) {
370               dst_offset = d + depth * (w + width * (h + n * height));
371               src_offset = MPA_L * (w + width * (h + (n + d / MPA_L) * height));
372
373               src_offset *= src_data_size;
374               dst_offset *= dst_data_size;
375
376               slice_size = (depth - d >= MPA_L) ? MPA_L : depth - d;
377               slice_size *= src_data_size;
378
379               if (quantizer)
380                 quantizer->memcpy (static_cast<char *> (dst_) + dst_offset,
381                                    static_cast<char *> (src_) + src_offset,
382                                    slice_size);
383               else
384                 memcpy (static_cast<char *> (dst_) + dst_offset,
385                         static_cast<char *> (src_) + src_offset, slice_size);
386             }
387           }
388         }
389       }
390     } else if (src_layout_ == DATA_LAYOUT_TRIV2 &&
391                dst_layout_ == DATA_LAYOUT_NCHW) {
392       /* TRIV2 --> NCHW */
393       slice_size = src_data_size;
394       for (uint32_t n = 0; n < batch; n++) {
395         for (uint32_t d = 0; d < depth; d++) {
396           for (uint32_t h = 0; h < height; h++) {
397             for (uint32_t w = 0; w < width; w++) {
398               dst_offset = w + width * (h + height * (d + n * depth));
399               src_offset = (d % MPA_L) +
400                            MPA_L * (w + width * (h + (n + d / MPA_L) * height));
401
402               src_offset *= src_data_size;
403               dst_offset *= dst_data_size;
404
405               if (quantizer)
406                 quantizer->memcpy (static_cast<char *> (dst_) + dst_offset,
407                                    static_cast<char *> (src_) + src_offset,
408                                    slice_size);
409               else
410                 memcpy (static_cast<char *> (dst_) + dst_offset,
411                         static_cast<char *> (src_) + src_offset, slice_size);
412             }
413           }
414         }
415       }
416     }
417     goto done;
418   }
419
420 try_quantize:
421   if (quantizer) {
422     quantizer->memcpy (dst_, src_, size_);
423   } else {
424     memcpy (dst_, src_, size_);
425   }
426
427 done:
428   if (quantizer)
429     delete quantizer;
430
431   return size_;
432 }