1 /* Copyright 2015 The TensorFlow 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
16 #define EIGEN_USE_THREADS
17
18 #include <functional>
19 #include <memory>
20 #include <vector>
21
22 #include "tensorflow/cc/client/client_session.h"
23 #include "tensorflow/cc/ops/array_ops.h"
24 #include "tensorflow/cc/ops/const_op.h"
25 #include "tensorflow/cc/ops/math_ops.h"
26 #include "tensorflow/core/framework/tensor_testutil.h"
27 #include "tensorflow/core/framework/types.h"
28 #include "tensorflow/core/framework/types.pb.h"
29 #include "tensorflow/core/kernels/ops_util.h"
30 #include "tensorflow/core/kernels/quantization_utils.h"
31 #include "tensorflow/core/lib/core/status_test_util.h"
32 #include "tensorflow/core/platform/test.h"
33
34 namespace tensorflow {
35 namespace ops {
36 namespace {
37
TestMul(const std::vector<int64> & x_shape,const std::vector<float> & x_values,float x_min_value,float x_max_value,const std::vector<int64> & y_shape,const std::vector<float> & y_values,float y_min_value,float y_max_value,const std::vector<int64> & expected_shape,const std::vector<float> & expected_values,double tolerance)38 void TestMul(const std::vector<int64>& x_shape,
39 const std::vector<float>& x_values, float x_min_value,
40 float x_max_value, const std::vector<int64>& y_shape,
41 const std::vector<float>& y_values, float y_min_value,
42 float y_max_value, const std::vector<int64>& expected_shape,
43 const std::vector<float>& expected_values, double tolerance) {
44 Scope root = Scope::NewRootScope();
45
46 Tensor x_float_tensor(DT_FLOAT, TensorShape(x_shape));
47 test::FillValues<float>(&x_float_tensor, x_values);
48 Tensor x_quantized_tensor(DT_QUINT8, x_float_tensor.shape());
49 FloatTensorToQuantizedInPlace<quint8>(x_float_tensor, x_min_value,
50 x_max_value, &x_quantized_tensor);
51 Output x =
52 Const(root.WithOpName("x"), Input::Initializer(x_quantized_tensor));
53 Output x_min = Const(root.WithOpName("x_min"), x_min_value);
54 Output x_max = Const(root.WithOpName("x_max"), x_max_value);
55
56 Tensor y_float_tensor(DT_FLOAT, TensorShape(y_shape));
57 test::FillValues<float>(&y_float_tensor, y_values);
58 Tensor y_quantized_tensor(DT_QUINT8, y_float_tensor.shape());
59 FloatTensorToQuantizedInPlace<quint8>(y_float_tensor, y_min_value,
60 y_max_value, &y_quantized_tensor);
61 Output y =
62 Const(root.WithOpName("y"), Input::Initializer(y_quantized_tensor));
63 Output y_min = Const(root.WithOpName("y_min"), y_min_value);
64 Output y_max = Const(root.WithOpName("y_max"), y_max_value);
65
66 QuantizedMul mul =
67 QuantizedMul(root.WithOpName("mul"), x, y, x_min, x_max, y_min, y_max);
68
69 TF_EXPECT_OK(root.status());
70
71 ClientSession session(root);
72 std::vector<Tensor> outputs;
73
74 TF_EXPECT_OK(session.Run(ClientSession::FeedType(),
75 {mul.z, mul.min_z, mul.max_z}, &outputs));
76
77 const Tensor& z_quantized = outputs[0];
78 const float z_min = outputs[1].flat<float>()(0);
79 const float z_max = outputs[2].flat<float>()(0);
80
81 Tensor z_float = QuantizedTensorToFloat<qint32>(z_quantized, z_min, z_max);
82 Tensor expected_z_float(DT_FLOAT, TensorShape(expected_shape));
83 test::FillValues<float>(&expected_z_float, expected_values);
84 test::ExpectTensorNear<float>(expected_z_float, z_float, tolerance);
85 }
86
TestMulShape(const std::vector<int64> & x_shape,const std::vector<int64> & y_shape)87 void TestMulShape(const std::vector<int64>& x_shape,
88 const std::vector<int64>& y_shape) {
89 const size_t x_num_elements = TensorShape(x_shape).num_elements();
90 std::vector<float> x_values(x_num_elements);
91 for (int i = 0; i < x_num_elements; ++i) {
92 x_values[i] = i % 256;
93 }
94 const float x_min_value = 0.0f;
95 const float x_max_value = 256.0f;
96
97 const size_t y_num_elements = TensorShape(y_shape).num_elements();
98 std::vector<float> y_values(y_num_elements);
99 for (int i = 0; i < y_num_elements; ++i) {
100 y_values[i] = ((i + 23) % 123) - 50;
101 }
102 const float y_min_value = -150.0f;
103 const float y_max_value = 150.0f;
104
105 Scope root = Scope::NewRootScope();
106
107 Tensor x_float_tensor(DT_FLOAT, TensorShape(x_shape));
108 test::FillValues<float>(&x_float_tensor, x_values);
109 Output x = Const(root.WithOpName("x"), Input::Initializer(x_float_tensor));
110
111 Tensor y_float_tensor(DT_FLOAT, TensorShape(y_shape));
112 test::FillValues<float>(&y_float_tensor, y_values);
113 Output y = Const(root.WithOpName("y"), Input::Initializer(y_float_tensor));
114
115 Mul mul = Mul(root.WithOpName("mul"), x, y);
116
117 TF_EXPECT_OK(root.status());
118
119 ClientSession session(root);
120 std::vector<Tensor> outputs;
121 TF_EXPECT_OK(session.Run(ClientSession::FeedType(), {mul.z}, &outputs));
122
123 const Tensor& expected_values_tensor = outputs[0];
124 const float* expected_values_data =
125 expected_values_tensor.flat<float>().data();
126 std::vector<float> expected_values(
127 expected_values_data,
128 expected_values_data + expected_values_tensor.NumElements());
129 std::vector<int64> expected_shape;
130 for (const int64_t dim : expected_values_tensor.shape().dim_sizes()) {
131 expected_shape.push_back(dim);
132 }
133 TestMul(x_shape, x_values, x_min_value, x_max_value, y_shape, y_values,
134 y_min_value, y_max_value, expected_shape, expected_values, 256.0);
135 }
136
TimeMul(const std::vector<int64> & x_shape,const std::vector<int64> & y_shape,int64_t iterations)137 void TimeMul(const std::vector<int64>& x_shape,
138 const std::vector<int64>& y_shape, int64_t iterations) {
139 TestMulShape(x_shape, y_shape);
140
141 Scope root = Scope::NewRootScope();
142
143 Tensor x_quantized_tensor(DT_QUINT8, TensorShape(x_shape));
144 Output placeholder = Placeholder(root.WithOpName("placeholder"), DT_QUINT8);
145 Output x_min = Const(root.WithOpName("x_min"), 0.0f);
146 Output x_max = Const(root.WithOpName("x_max"), 1.0f);
147
148 Tensor y_quantized_tensor(DT_QUINT8, TensorShape(y_shape));
149 Output y =
150 Const(root.WithOpName("y"), Input::Initializer(y_quantized_tensor));
151 Output y_min = Const(root.WithOpName("y_min"), 0.0f);
152 Output y_max = Const(root.WithOpName("y_max"), 1.0f);
153
154 QuantizedMul mul = QuantizedMul(root.WithOpName("mul"), placeholder, y, x_min,
155 x_max, y_min, y_max);
156
157 TF_EXPECT_OK(root.status());
158
159 ClientSession session(root);
160 std::vector<Tensor> outputs;
161
162 int64_t total_duration = 0;
163 for (int i = 0; i < iterations; ++i) {
164 const int64_t start_time = Env::Default()->NowMicros();
165 TF_EXPECT_OK(session.Run({{placeholder, x_quantized_tensor}},
166 {mul.z, mul.min_z, mul.max_z}, &outputs));
167 const int64_t end_time = Env::Default()->NowMicros();
168 total_duration += end_time - start_time;
169 }
170 const int64_t one_run_duration = total_duration / iterations;
171
172 const int64_t num_ops = outputs[0].NumElements();
173
174 const double million_ops_per_second =
175 (iterations * num_ops) / static_cast<double>(total_duration);
176
177 LOG(INFO) << "TimeMul: " << TensorShape(x_shape).DebugString() << " * "
178 << TensorShape(y_shape).DebugString()
179 << ": iterations=" << iterations
180 << ", MOps/s=" << million_ops_per_second
181 << ", one_run_duration=" << one_run_duration
182 << ", total_duration=" << total_duration;
183 }
184
TestManualScalar()185 void TestManualScalar() {
186 TestMul(
187 {10}, {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f}, 0.0f,
188 10.0f, {1}, {10.0f}, -100.0f, 100.0f, {10},
189 {10.0f, 20.0f, 30.0f, 40.0f, 50.0f, 60.0f, 70.0f, 80.0f, 90.0f, 100.0f},
190 3.0f);
191 TestMul(
192 {1}, {10.0f}, -100.0f, 100.0f, {10},
193 {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f}, 0.0f,
194 10.0f, {10},
195 {10.0f, 20.0f, 30.0f, 40.0f, 50.0f, 60.0f, 70.0f, 80.0f, 90.0f, 100.0f},
196 3.0f);
197 }
198
TestScalar()199 void TestScalar() {
200 TestMulShape({100}, {1});
201 TestMulShape({1}, {100});
202 }
203
TestManualVector()204 void TestManualVector() {
205 TestMul({10}, {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f},
206 0.0f, 10.0f, {10},
207 {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f}, 0.0f,
208 10.0f, {10},
209 {1.0f, 4.0f, 9.0f, 16.0f, 25.0f, 36.0f, 49.0f, 64.0f, 81.0f, 100.0f},
210 3.0f);
211 }
212
TestVector()213 void TestVector() { TestMulShape({100}, {100}); }
214
TestManualVectorTimesTensor()215 void TestManualVectorTimesTensor() {
216 TestMul(
217 {10}, {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f}, 0.0f,
218 10.0f, {2, 10},
219 {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f,
220 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f, 18.0f, 19.0f, 20.0f},
221 0.0f, 20.0f, {2, 10}, {1.0f, 4.0f, 9.0f, 16.0f, 25.0f, 36.0f, 49.0f,
222 64.0f, 81.0f, 100.0f, 11.0f, 24.0f, 39.0f, 56.0f,
223 75.0f, 96.0f, 119.0f, 144.0f, 171.0f, 200.0f},
224 3.0f);
225 TestMul({2, 10}, {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f,
226 8.0f, 9.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f,
227 15.0f, 16.0f, 17.0f, 18.0f, 19.0f, 20.0f},
228 0.0f, 20.0f, {10},
229 {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f}, 0.0f,
230 10.0f, {2, 10}, {1.0f, 4.0f, 9.0f, 16.0f, 25.0f, 36.0f, 49.0f,
231 64.0f, 81.0f, 100.0f, 11.0f, 24.0f, 39.0f, 56.0f,
232 75.0f, 96.0f, 119.0f, 144.0f, 171.0f, 200.0f},
233 3.0f);
234 TestMul(
235 {5, 2}, {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f},
236 0.0f, 10.0f, {2, 5, 2},
237 {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f,
238 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f, 18.0f, 19.0f, 20.0f},
239 0.0f, 20.0f, {2, 5, 2},
240 {1.0f, 4.0f, 9.0f, 16.0f, 25.0f, 36.0f, 49.0f,
241 64.0f, 81.0f, 100.0f, 11.0f, 24.0f, 39.0f, 56.0f,
242 75.0f, 96.0f, 119.0f, 144.0f, 171.0f, 200.0f},
243 3.0f);
244 }
245
TestVectorTimesTensor()246 void TestVectorTimesTensor() {
247 TestMulShape({100}, {2, 100});
248 TestMulShape({2, 100}, {100});
249 TestMulShape({5, 2}, {2, 5, 2});
250 }
251
BenchmarkTensorScalar()252 void BenchmarkTensorScalar() {
253 TimeMul({200}, {1}, 10000);
254 TimeMul({10000}, {1}, 1000);
255 TimeMul({1000000}, {1}, 100);
256 TimeMul({10000000}, {1}, 100);
257 }
258
BenchmarkVector()259 void BenchmarkVector() {
260 TimeMul({200}, {200}, 10000);
261 TimeMul({10000}, {10000}, 1000);
262 TimeMul({1000000}, {1000000}, 100);
263 TimeMul({10000000}, {10000000}, 100);
264 }
265
BenchmarkVectorTimesTensor()266 void BenchmarkVectorTimesTensor() {
267 TimeMul({10, 20}, {20}, 10000);
268 TimeMul({10, 1000}, {1000}, 1000);
269 TimeMul({1000, 1000}, {1000}, 100);
270 TimeMul({10000, 1000}, {1000}, 100);
271 TimeMul({100, 100}, {100}, 1000);
272 TimeMul({10000, 100}, {100}, 100);
273 TimeMul({100000, 100}, {100}, 100);
274 }
275
276 } // namespace
277 } // namespace ops
278 } // namespace tensorflow
279
280 #define RUN_TEST(t) \
281 TEST(QuantizedAddOpTest, t) { tensorflow::ops::t(); }
282
283 RUN_TEST(TestManualScalar);
284 RUN_TEST(TestManualVector);
285 RUN_TEST(TestManualVectorTimesTensor);
286 RUN_TEST(TestScalar);
287 RUN_TEST(TestVector);
288 RUN_TEST(TestVectorTimesTensor);
289
290 #if defined(__ANDROID__)
291
292 RUN_TEST(BenchmarkTensorScalar);
293 RUN_TEST(BenchmarkVector);
294 RUN_TEST(BenchmarkVectorTimesTensor);
295
296 #endif // __ANDROID__
297
main(int argc,char ** argv)298 int main(int argc, char** argv) {
299 // On Linux, add: absl::SetFlag(&FLAGS_logtostderr, true);
300 ::testing::InitGoogleTest(&argc, argv);
301 return RUN_ALL_TESTS();
302 }
303