1 // Copyright 2016 The Gemmlowp Authors. All Rights Reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include <unistd.h>
16 #ifdef __APPLE__
17 #include <sys/time.h>
18 #endif
19
20 #include <cstdint>
21 #include <cstdlib>
22 #include <ctime>
23 #include <iomanip>
24 #include <iostream>
25 #include <map>
26 #include <memory>
27 #include <vector>
28
29 #include "single_thread_transform.h"
30 #include "transform_kernels.h"
31
32 #define EPSILON (0.0001)
33
34 using namespace gemmlowp::meta;
35
36 typedef Transform1DParams<std::int32_t, std::uint8_t, Requantize> RequantizeParams;
37 typedef Transform1DParams<float, std::uint8_t, Quantize> QuantizeParams;
38 typedef Transform1DParams<std::uint8_t, float, Dequantize> DequantizeParams;
39 typedef Transform1DParams<std::uint8_t, std::uint8_t, MinMax<std::uint8_t>> MinMaxParams;
40 typedef Transform1DParams<std::uint8_t, std::int32_t, BiasAdd<std::uint8_t>> BiasAddParams;
41
prepare_data_requantize(int count,std::int32_t * data)42 void prepare_data_requantize(int count, std::int32_t* data) {
43 float scale = 4000000000.0f / static_cast<float>(count - 1);
44 for (int i = 0; i < count; ++i) {
45 float temp = -2000000000.0f + scale * i;
46 data[i] = static_cast<std::int32_t>(temp);
47 }
48 }
49
prepare_data_quantize(int count,float * data)50 void prepare_data_quantize(int count, float* data) {
51 float scale = 200.0f / static_cast<float>(count - 1);
52 for (int i = 0; i < count; ++i) {
53 data[i] = -100 + scale * i;
54 }
55 }
56
prepare_data_dequantize(int count,std::uint8_t * data)57 void prepare_data_dequantize(int count, std::uint8_t* data) {
58 for (int i = 0; i < count; ++i) {
59 data[i] = static_cast<std::uint8_t>(i % 256);
60 }
61 }
62
prepare_data_minmax(int count,std::uint8_t * data)63 void prepare_data_minmax(int count, std::uint8_t* data) {
64 for (int i = 0; i < count; ++i) {
65 data[i] = static_cast<std::uint8_t>(i % 256);
66 }
67 }
68
prepare_data_biasadd(int count,std::uint8_t * data)69 void prepare_data_biasadd(int count, std::uint8_t* data) {
70 for (int i = 0; i < count; ++i) {
71 data[i] = static_cast<std::uint8_t>(i % 256);
72 }
73 }
74
verify_requantize(const RequantizeParams & params)75 void verify_requantize(const RequantizeParams& params) {
76 for (int i = 0; i < params.kernel.count; ++i) {
77 std::uint8_t actual = params.output[i];
78 float expected = static_cast<float>(params.input[i]);
79 expected -= params.kernel.input_range_offset;
80 expected *= params.kernel.input_range_scale;
81 expected += params.kernel.input_range_min;
82 expected -= params.kernel.output_range_min;
83 expected *= params.kernel.one_over_output_range_scale;
84 expected += params.kernel.output_range_offset;
85 std::uint8_t expected_uint8 = static_cast<std::uint8_t>(expected);
86
87 if (actual != expected_uint8) {
88 std::cout << "Wrong: " << i << " : " << actual << " vs. "
89 << expected_uint8 << std::endl;
90 std::exit(1);
91 }
92 }
93 std::cout << "Requantize: OK" << std::endl;
94 }
95
verify_quantize(const QuantizeParams & params)96 void verify_quantize(const QuantizeParams& params) {
97 for (int i = 0; i < params.kernel.count; ++i) {
98 std::uint8_t actual = params.output[i];
99 float expected = params.input[i];
100 expected -= params.kernel.range_min;
101 expected *= params.kernel.range_scale;
102 expected += params.kernel.range_offset;
103 std::uint8_t expected_uint8 = static_cast<std::uint8_t>(expected);
104
105 if (actual != expected_uint8) {
106 std::cout << "Wrong: " << i << " : " << actual << " vs. "
107 << expected_uint8 << std::endl;
108 std::exit(1);
109 }
110 }
111 std::cout << "Quantize: OK" << std::endl;
112 }
113
verify_dequantize(const DequantizeParams & params)114 void verify_dequantize(const DequantizeParams& params) {
115 for (int i = 0; i < params.kernel.count; ++i) {
116 float actual = params.output[i];
117 float expected = static_cast<float>(params.input[i]);
118 expected -= params.kernel.range_offset;
119 expected *= params.kernel.range_scale;
120 expected += params.kernel.range_min;
121 if (std::abs(actual - expected) > EPSILON) {
122 std::cout << std::setprecision(9) << "Wrong: " << i << " : " << actual
123 << " vs. " << expected << std::endl;
124 std::exit(1);
125 }
126 }
127 std::cout << "Dequantize: OK" << std::endl;
128 }
129
verify_minmax(const MinMaxParams & params)130 void verify_minmax(const MinMaxParams& params) {
131 for (int i = 0; i < params.kernel.count; ++i) {
132 std::uint8_t actual = params.output[i];
133 std::uint8_t expected = params.input[i];
134 expected = std::min(expected, params.kernel.max);
135 expected = std::max(expected, params.kernel.min);
136
137 if (actual != expected) {
138 std::cout << "Wrong: " << i << " : " << actual << " vs. " << expected
139 << std::endl;
140 std::exit(1);
141 }
142 }
143 std::cout << "MinMax: OK" << std::endl;
144 }
145
verify_biasadd(const BiasAddParams & params)146 void verify_biasadd(const BiasAddParams& params) {
147 for (int i = 0; i < params.kernel.rows * params.kernel.count; ++i) {
148 std::int32_t actual = params.output[i];
149 std::uint8_t input = params.input[i];
150 std::uint8_t bias = params.kernel.bias[i % params.kernel.count];
151 float input_float = static_cast<float>(input);
152 input_float -= params.kernel.input_range_offset;
153 input_float *= params.kernel.input_range_scale;
154 input_float += params.kernel.input_range_min;
155 float bias_float = static_cast<float>(bias);
156 bias_float -= params.kernel.bias_range_offset;
157 bias_float *= params.kernel.bias_range_scale;
158 bias_float += params.kernel.bias_range_min;
159 float sum = input_float + bias_float;
160 sum -= params.kernel.output_range_min;
161 sum *= params.kernel.one_over_output_range_scale;
162 sum += params.kernel.output_range_offset;
163 std::int32_t expected = static_cast<std::int32_t>(sum);
164 if (std::abs(actual - expected) > 1024) {
165 std::cout << "Wrong: " << i << " : " << actual << " vs. " << expected
166 << std::endl;
167 std::exit(1);
168 }
169 }
170 std::cout << "BiasAdd: OK" << std::endl;
171 }
172
main()173 int main() {
174 std::unique_ptr<std::int32_t[]> array_int32(new std::int32_t[128 * 1024]);
175 std::unique_ptr<std::uint8_t[]> array_uint8(new std::uint8_t[128 * 1024]);
176 std::unique_ptr<std::uint8_t[]> array_uint8_2(new std::uint8_t[128 * 1024]);
177 std::unique_ptr<float[]> array_float(new float[128 * 1024]);
178
179 {
180 RequantizeParams requantize_params;
181 requantize_params.input = array_int32.get();
182 requantize_params.output = array_uint8.get();
183 requantize_params.kernel.count = 12345;
184 requantize_params.kernel.input_range_min = -100.0f;
185 requantize_params.kernel.input_range_scale =
186 200.0f / ((static_cast<std::int64_t>(1) << 32) - 1);
187 requantize_params.kernel.input_range_offset =
188 static_cast<float>(std::numeric_limits<std::int32_t>::lowest());
189 requantize_params.kernel.output_range_min = -100.f;
190 requantize_params.kernel.one_over_output_range_scale =
191 static_cast<float>((static_cast<std::int64_t>(1) << 8) - 1) / 200.0f;
192 requantize_params.kernel.output_range_offset =
193 static_cast<float>(std::numeric_limits<std::uint8_t>::lowest());
194
195 prepare_data_requantize(12345, array_int32.get());
196
197 Transform1D<RequantizeParams, 16>(requantize_params);
198
199 verify_requantize(requantize_params);
200 }
201
202 {
203 QuantizeParams quantize_params;
204 quantize_params.input = array_float.get();
205 quantize_params.output = array_uint8.get();
206 quantize_params.kernel.count = 12345;
207 quantize_params.kernel.range_min = -100.0f;
208 quantize_params.kernel.range_scale =
209 static_cast<float>((static_cast<std::int64_t>(1) << 8) - 1) / 200.0f;
210 quantize_params.kernel.range_offset =
211 static_cast<float>(std::numeric_limits<std::uint8_t>::lowest());
212
213 prepare_data_quantize(12345, array_float.get());
214
215 Transform1D<QuantizeParams, 16>(quantize_params);
216
217 verify_quantize(quantize_params);
218 }
219
220 {
221 DequantizeParams dequantize_params;
222 dequantize_params.input = array_uint8.get();
223 dequantize_params.output = array_float.get();
224 dequantize_params.kernel.count = 12345;
225 dequantize_params.kernel.range_min = -100.0f;
226 dequantize_params.kernel.range_scale =
227 200.0f / ((static_cast<std::int64_t>(1) << 8) - 1);
228 dequantize_params.kernel.range_offset =
229 static_cast<float>(std::numeric_limits<std::uint8_t>::lowest());
230
231 prepare_data_dequantize(12345, array_uint8.get());
232
233 Transform1D<DequantizeParams, 16>(dequantize_params);
234
235 verify_dequantize(dequantize_params);
236 }
237
238 {
239 MinMaxParams minmax_params;
240 minmax_params.input = array_uint8.get();
241 minmax_params.output = array_uint8_2.get();
242 minmax_params.kernel.count = 12345;
243 minmax_params.kernel.min = 64;
244 minmax_params.kernel.max = 192;
245
246 prepare_data_minmax(12345, array_uint8.get());
247
248 Transform1D<MinMaxParams, 16>(minmax_params);
249
250 verify_minmax(minmax_params);
251 }
252
253 {
254 BiasAddParams biasadd_params;
255 biasadd_params.input = array_uint8.get();
256 biasadd_params.output = array_int32.get();
257 biasadd_params.kernel.count = 1234;
258 biasadd_params.kernel.rows = 11;
259 biasadd_params.kernel.input_range_min = -100.0f;
260 biasadd_params.kernel.bias_range_min = -100.0f;
261 biasadd_params.kernel.output_range_min = -250.0f;
262 biasadd_params.kernel.input_range_offset =
263 static_cast<float>(std::numeric_limits<std::uint8_t>::lowest());
264 biasadd_params.kernel.bias_range_offset =
265 static_cast<float>(std::numeric_limits<std::uint8_t>::lowest());
266 biasadd_params.kernel.output_range_offset =
267 static_cast<float>(std::numeric_limits<std::int32_t>::lowest());
268 biasadd_params.kernel.input_range_scale =
269 200.0f / ((static_cast<std::int64_t>(1) << 8) - 1);
270 biasadd_params.kernel.bias_range_scale =
271 200.0f / ((static_cast<std::int64_t>(1) << 8) - 1);
272 biasadd_params.kernel.one_over_output_range_scale =
273 static_cast<float>((static_cast<std::int64_t>(1) << 32) - 1) / 500.0f;
274 biasadd_params.kernel.bias = array_uint8_2.get();
275
276 prepare_data_biasadd(1234 * 11, array_uint8.get());
277 prepare_data_biasadd(1234, array_uint8_2.get());
278
279 Transform1D<BiasAddParams, 16>(biasadd_params);
280
281 verify_biasadd(biasadd_params);
282 }
283
284 return 0;
285 }
286