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 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 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 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 137 void TimeMul(const std::vector<int64>& x_shape, 138 const std::vector<int64>& y_shape, int64 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 total_duration = 0; 163 for (int i = 0; i < iterations; ++i) { 164 const int64 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 end_time = Env::Default()->NowMicros(); 168 total_duration += end_time - start_time; 169 } 170 const int64 one_run_duration = total_duration / iterations; 171 172 const int64 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 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 199 void TestScalar() { 200 TestMulShape({100}, {1}); 201 TestMulShape({1}, {100}); 202 } 203 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 213 void TestVector() { TestMulShape({100}, {100}); } 214 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 246 void TestVectorTimesTensor() { 247 TestMulShape({100}, {2, 100}); 248 TestMulShape({2, 100}, {100}); 249 TestMulShape({5, 2}, {2, 5, 2}); 250 } 251 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 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 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 298 int main(int argc, char** argv) { 299 // On Linux, add: FLAGS_logtostderr = true; 300 ::testing::InitGoogleTest(&argc, argv); 301 return RUN_ALL_TESTS(); 302 } 303