Home | History | Annotate | Download | only in kernels
      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