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 TestAdd(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 ops::QuantizedAdd add = ops::QuantizedAdd(root.WithOpName("add"), x, y, x_min, 67 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 {add.z, add.min_z, add.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 TestAddShape(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 Add add = Add(root.WithOpName("add"), 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(), {add.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 TestAdd(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 TimeAdd(const std::vector<int64>& x_shape, 138 const std::vector<int64>& y_shape, int64 iterations) { 139 TestAddShape(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 ops::QuantizedAdd add = ops::QuantizedAdd(root.WithOpName("add"), placeholder, 155 y, x_min, 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 {add.z, add.min_z, add.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) << "TimeAdd: " << 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 TestAdd( 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 {11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f, 18.0f, 19.0f, 20.0f}, 190 1.0f); 191 TestAdd( 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 {11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f, 18.0f, 19.0f, 20.0f}, 196 1.0f); 197 } 198 199 void TestScalar() { 200 TestAddShape({100}, {1}); 201 TestAddShape({1}, {100}); 202 } 203 204 void TestManualVector() { 205 TestAdd({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 {2.0f, 4.0f, 6.0f, 8.0f, 10.0f, 12.0f, 14.0f, 16.0f, 18.0f, 20.0f}, 210 1.0f); 211 } 212 213 void TestVector() { TestAddShape({100}, {100}); } 214 215 void TestManualVectorPlusTensor() { 216 TestAdd( 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}, 222 {2.0f, 4.0f, 6.0f, 8.0f, 10.0f, 12.0f, 14.0f, 16.0f, 18.0f, 20.0f, 223 12.0f, 14.0f, 16.0f, 18.0f, 20.0f, 22.0f, 24.0f, 26.0f, 28.0f, 30.0f}, 224 1.0f); 225 TestAdd({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}, {2.0f, 4.0f, 6.0f, 8.0f, 10.0f, 12.0f, 14.0f, 231 16.0f, 18.0f, 20.0f, 12.0f, 14.0f, 16.0f, 18.0f, 232 20.0f, 22.0f, 24.0f, 26.0f, 28.0f, 30.0f}, 233 1.0f); 234 TestAdd( 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 {2.0f, 4.0f, 6.0f, 8.0f, 10.0f, 12.0f, 14.0f, 16.0f, 18.0f, 20.0f, 241 12.0f, 14.0f, 16.0f, 18.0f, 20.0f, 22.0f, 24.0f, 26.0f, 28.0f, 30.0f}, 242 1.0f); 243 } 244 245 void TestVectorPlusTensor() { 246 TestAddShape({100}, {2, 100}); 247 TestAddShape({2, 100}, {100}); 248 TestAddShape({5, 2}, {2, 5, 2}); 249 } 250 251 void BenchmarkTensorScalar() { 252 TimeAdd({200}, {1}, 1000); 253 TimeAdd({10000}, {1}, 100); 254 TimeAdd({1000000}, {1}, 10); 255 TimeAdd({10000000}, {1}, 1); 256 } 257 258 void BenchmarkVector() { 259 TimeAdd({200}, {200}, 1000); 260 TimeAdd({10000}, {10000}, 100); 261 TimeAdd({1000000}, {1000000}, 10); 262 TimeAdd({10000000}, {10000000}, 1); 263 } 264 265 void BenchmarkVectorPlusTensor() { 266 TimeAdd({10, 20}, {20}, 100); 267 TimeAdd({10, 1000}, {1000}, 10); 268 TimeAdd({1000, 1000}, {1000}, 1); 269 TimeAdd({10000, 1000}, {1000}, 1); 270 TimeAdd({100, 100}, {100}, 10); 271 TimeAdd({10000, 100}, {100}, 1); 272 TimeAdd({100000, 100}, {100}, 1); 273 } 274 275 } // namespace 276 } // namespace ops 277 } // namespace tensorflow 278 279 #define RUN_TEST(t) \ 280 TEST(QuantizedAddOpTest, t) { tensorflow::ops::t(); } 281 282 RUN_TEST(TestManualScalar); 283 RUN_TEST(TestManualVector); 284 RUN_TEST(TestManualVectorPlusTensor); 285 RUN_TEST(TestScalar); 286 RUN_TEST(TestVector); 287 RUN_TEST(TestVectorPlusTensor); 288 289 #if defined(__ANDROID__) 290 291 RUN_TEST(BenchmarkTensorScalar); 292 RUN_TEST(BenchmarkVector); 293 RUN_TEST(BenchmarkVectorPlusTensor); 294 295 #endif // __ANDROID__ 296 297 int main(int argc, char** argv) { 298 // On Linux, add: FLAGS_logtostderr = true; 299 ::testing::InitGoogleTest(&argc, argv); 300 return RUN_ALL_TESTS(); 301 } 302