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 #include <functional> 17 #include <memory> 18 #include <vector> 19 20 #include "tensorflow/cc/ops/array_ops.h" 21 #include "tensorflow/cc/ops/const_op.h" 22 #include "tensorflow/core/common_runtime/kernel_benchmark_testlib.h" 23 #include "tensorflow/core/framework/allocator.h" 24 #include "tensorflow/core/framework/fake_input.h" 25 #include "tensorflow/core/framework/node_def_builder.h" 26 #include "tensorflow/core/framework/op_kernel.h" 27 #include "tensorflow/core/framework/tensor.h" 28 #include "tensorflow/core/framework/tensor_testutil.h" 29 #include "tensorflow/core/framework/types.h" 30 #include "tensorflow/core/kernels/ops_testutil.h" 31 #include "tensorflow/core/lib/core/status_test_util.h" 32 #include "tensorflow/core/platform/test_benchmark.h" 33 34 namespace tensorflow { 35 namespace { 36 37 class DequantizeOpTest : public OpsTestBase { 38 protected: 39 template <typename T> 40 void ComputeDequantizeMinCombinedUsingEigen(const Tensor& input, 41 float min_range, float max_range, 42 Tensor* output) { 43 float half_range = 44 !std::is_signed<T>::value 45 ? 0.0f 46 : (static_cast<float>(std::numeric_limits<T>::max()) - 47 std::numeric_limits<T>::min() + 1) / 48 2.0f; 49 const float scale_factor = 50 (max_range - min_range) / 51 (static_cast<float>(std::numeric_limits<T>::max()) - 52 std::numeric_limits<T>::min()); 53 output->flat<float>() = 54 ((input.flat<T>().template cast<int>().template cast<float>() + 55 half_range) * 56 scale_factor) + 57 min_range; 58 } 59 60 // Compares dequantize min vs the same using eigen. This tests that a change 61 // to not use eigen gives equivalent results to using eigen. 62 template <typename T> 63 void RunDequantizeMinCombinedTest(float min_range, float max_range) { 64 TF_ASSERT_OK(NodeDefBuilder("dequantize_op", "Dequantize") 65 .Input(FakeInput(DataTypeToEnum<T>::v())) 66 .Input(FakeInput(DT_FLOAT)) 67 .Input(FakeInput(DT_FLOAT)) 68 .Attr("T", DataTypeToEnum<T>::v()) 69 .Attr("mode", "MIN_COMBINED") 70 .Finalize(node_def())); 71 TF_ASSERT_OK(InitOp()); 72 73 std::vector<T> input; 74 for (int64 i = std::numeric_limits<T>::min(); 75 i < std::numeric_limits<T>::max(); ++i) { 76 input.push_back(static_cast<T>(i)); 77 } 78 TensorShape shape({static_cast<int64>(input.size())}); 79 AddInputFromArray<T>(shape, input); 80 AddInputFromArray<float>(TensorShape({}), {min_range}); 81 AddInputFromArray<float>(TensorShape({}), {max_range}); 82 TF_ASSERT_OK(RunOpKernel()); 83 Tensor expected(allocator(), DT_FLOAT, shape); 84 ComputeDequantizeMinCombinedUsingEigen<T>(GetInput(0), min_range, max_range, 85 &expected); 86 test::ExpectTensorEqual<float>(expected, *GetOutput(0)); 87 } 88 89 template <typename T> 90 void RunDequantizeScaledTest(float min_range, float max_range, int input_int, 91 float expected_output) { 92 TF_ASSERT_OK(NodeDefBuilder("dequantize_op", "Dequantize") 93 .Input(FakeInput(DataTypeToEnum<T>::v())) 94 .Input(FakeInput(DT_FLOAT)) 95 .Input(FakeInput(DT_FLOAT)) 96 .Attr("T", DataTypeToEnum<T>::v()) 97 .Attr("mode", "SCALED") 98 .Finalize(node_def())); 99 TF_ASSERT_OK(InitOp()); 100 101 std::vector<T> input; 102 input.push_back(static_cast<T>(input_int)); 103 TensorShape shape({static_cast<int64>(input.size())}); 104 AddInputFromArray<T>(shape, input); 105 AddInputFromArray<float>(TensorShape({}), {min_range}); 106 AddInputFromArray<float>(TensorShape({}), {max_range}); 107 TF_ASSERT_OK(RunOpKernel()); 108 Tensor expected(allocator(), DT_FLOAT, shape); 109 test::FillValues<float>(&expected, {expected_output}); 110 test::ExpectClose(expected, *GetOutput(0)); 111 } 112 }; 113 114 TEST_F(DequantizeOpTest, DequantizeMinCombinedQuint8) { 115 RunDequantizeMinCombinedTest<quint8>(0, 255.0f); 116 } 117 TEST_F(DequantizeOpTest, DequantizeMinCombinedQint8) { 118 RunDequantizeMinCombinedTest<qint8>(0, 255.0f); 119 } 120 TEST_F(DequantizeOpTest, DequantizeMinCombinedQint16) { 121 RunDequantizeMinCombinedTest<qint16>(0, 255.0f); 122 } 123 TEST_F(DequantizeOpTest, DequantizeMinCombinedQuint16) { 124 RunDequantizeMinCombinedTest<quint16>(0, 255.0f); 125 } 126 127 TEST_F(DequantizeOpTest, DequantizeScaledQuint8Zero) { 128 RunDequantizeScaledTest<quint8>(-255.0f, 127.0f, 0, 0.0); 129 } 130 TEST_F(DequantizeOpTest, DequantizeScaledQuint8ScaleIdentity) { 131 RunDequantizeScaledTest<quint8>(-255.0f, 127.0f, 127, 127.0); 132 } 133 TEST_F(DequantizeOpTest, DequantizeScaledQuint8ScaleDown) { 134 RunDequantizeScaledTest<quint8>(-1.0f, 2.0f, 255, 2.0); 135 } 136 TEST_F(DequantizeOpTest, DequantizeScaledQuint8ScaleUp) { 137 RunDequantizeScaledTest<quint8>(200.0f, 400.0f, 255, 400.0); 138 } 139 140 TEST_F(DequantizeOpTest, DequantizeScaledQint8Zero) { 141 RunDequantizeScaledTest<qint8>(-255.0f, 127.0f, 0, 0.0); 142 } 143 TEST_F(DequantizeOpTest, DequantizeScaledQint8ScaleIdentity) { 144 RunDequantizeScaledTest<qint8>(-10.0f, 127.0f, -127, -127.0); 145 } 146 TEST_F(DequantizeOpTest, DequantizeScaledQint8ScaleDown) { 147 RunDequantizeScaledTest<qint8>(-2.0f, 1.0f, -127, -2.0); 148 } 149 TEST_F(DequantizeOpTest, DequantizeScaledQint8ScaleUp) { 150 RunDequantizeScaledTest<qint8>(-1.0f, 300.0f, 42, 99.212601); 151 } 152 153 template <typename T> 154 static void BM_DequantizeMinCombinedCpu(int iters) { 155 auto root = Scope::NewRootScope().ExitOnError(); 156 const int64 num_values = 1500 * 250; 157 std::vector<T> inputs; 158 inputs.reserve(num_values); 159 for (int i = 0; i < num_values; ++i) inputs.push_back(i); 160 ops::Dequantize(root, test::AsTensor<T>(inputs), test::AsScalar<float>(-1.5f), 161 test::AsScalar<float>(20.5f), 162 ops::Dequantize::Attrs().Mode("MIN_COMBINED")); 163 TF_CHECK_OK(root.status()); 164 Graph* g = new Graph(OpRegistry::Global()); 165 TF_CHECK_OK(root.ToGraph(g)); 166 167 test::Benchmark("cpu", g).Run(iters); 168 testing::BytesProcessed(iters * num_values * (sizeof(float) + sizeof(T))); 169 testing::ItemsProcessed(iters); 170 } 171 172 static void BM_DequantizeMinCombinedCpuQuint16(int iters) { 173 BM_DequantizeMinCombinedCpu<quint16>(iters); 174 } 175 176 static void BM_DequantizeMinCombinedCpuQint16(int iters) { 177 BM_DequantizeMinCombinedCpu<qint16>(iters); 178 } 179 180 static void BM_DequantizeMinCombinedCpuQuint8(int iters) { 181 BM_DequantizeMinCombinedCpu<quint8>(iters); 182 } 183 184 static void BM_DequantizeMinCombinedCpuQint8(int iters) { 185 BM_DequantizeMinCombinedCpu<qint8>(iters); 186 } 187 188 BENCHMARK(BM_DequantizeMinCombinedCpuQuint16); 189 BENCHMARK(BM_DequantizeMinCombinedCpuQint16); 190 BENCHMARK(BM_DequantizeMinCombinedCpuQuint8); 191 BENCHMARK(BM_DequantizeMinCombinedCpuQint8); 192 193 } // namespace 194 } // namespace tensorflow 195