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 <vector> 18 19 #include "tensorflow/core/common_runtime/device.h" 20 #include "tensorflow/core/common_runtime/device_factory.h" 21 #include "tensorflow/core/framework/allocator.h" 22 #include "tensorflow/core/framework/fake_input.h" 23 #include "tensorflow/core/framework/node_def_builder.h" 24 #include "tensorflow/core/framework/op_kernel.h" 25 #include "tensorflow/core/framework/tensor.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_testutil.h" 30 #include "tensorflow/core/kernels/ops_util.h" 31 #include "tensorflow/core/platform/test.h" 32 #include "tensorflow/core/platform/test_benchmark.h" 33 #include "tensorflow/core/public/session.h" 34 35 namespace tensorflow { 36 37 namespace { 38 39 class SparseToDenseTest : public OpsTestBase { 40 protected: 41 void MakeOp(int dim, DataType index_type, DataType value_type) { 42 TF_ASSERT_OK(NodeDefBuilder("sparsetodense", "SparseToDense") 43 .Input(FakeInput(index_type)) 44 .Input(FakeInput(index_type)) 45 .Input(FakeInput(value_type)) 46 .Input(FakeInput(value_type)) 47 .Finalize(node_def())); 48 TF_ASSERT_OK(InitOp()); 49 } 50 }; 51 52 TEST_F(SparseToDenseTest, OneD_OneValue) { 53 MakeOp(1, DT_INT32, DT_FLOAT); 54 55 // sparse_indices 56 AddInputFromArray<int32>(TensorShape({3}), {1, 3, 4}); 57 // output_shape 58 AddInputFromArray<int32>(TensorShape({1}), {5}); 59 // sparse_values 60 AddInputFromArray<float>(TensorShape({}), {2}); 61 // default_value 62 AddInputFromArray<float>(TensorShape({}), {-2}); 63 64 TF_ASSERT_OK(RunOpKernel()); 65 66 Tensor expected(allocator(), DT_FLOAT, {5}); 67 test::FillValues<float>(&expected, {-2, 2, -2, 2, 2}); 68 test::ExpectTensorEqual<float>(expected, *GetOutput(0)); 69 } 70 71 TEST_F(SparseToDenseTest, OneD_OneValue_int64_double) { 72 MakeOp(1, DT_INT64, DT_DOUBLE); 73 74 // sparse_indices 75 AddInputFromArray<int64>(TensorShape({3}), {1, 3, 4}); 76 // output_shape 77 AddInputFromArray<int64>(TensorShape({1}), {5}); 78 // sparse_values 79 AddInputFromArray<double>(TensorShape({}), {2}); 80 // default_value 81 AddInputFromArray<double>(TensorShape({}), {-2}); 82 83 TF_ASSERT_OK(RunOpKernel()); 84 85 Tensor expected(allocator(), DT_DOUBLE, {5}); 86 test::FillValues<double>(&expected, {-2, 2, -2, 2, 2}); 87 test::ExpectTensorEqual<double>(expected, *GetOutput(0)); 88 } 89 90 TEST_F(SparseToDenseTest, OneD_MultValues) { 91 MakeOp(1, DT_INT32, DT_FLOAT); 92 93 // sparse_indices 94 AddInputFromArray<int32>({3}, {1, 3, 4}); 95 // output_shape 96 AddInputFromArray<int32>({1}, {5}); 97 // sparse_values 98 AddInputFromArray<float>({3}, {3, 4, 5}); 99 // default_value 100 AddInputFromArray<float>({}, {-2}); 101 102 TF_ASSERT_OK(RunOpKernel()); 103 104 Tensor expected(allocator(), DT_FLOAT, {5}); 105 test::FillValues<float>(&expected, {-2, 3, -2, 4, 5}); 106 test::ExpectTensorEqual<float>(expected, *GetOutput(0)); 107 } 108 109 TEST_F(SparseToDenseTest, TwoD_OneValue) { 110 MakeOp(2, DT_INT32, DT_FLOAT); 111 112 // sparse_indices 113 AddInputFromArray<int32>(TensorShape({3, 2}), {0, 1, 0, 2, 2, 3}); 114 // output_shape 115 AddInputFromArray<int32>(TensorShape({2}), {3, 4}); 116 // sparse_values 117 AddInputFromArray<float>(TensorShape({}), {2}); 118 // default_value 119 AddInputFromArray<float>(TensorShape({}), {-2}); 120 121 TF_ASSERT_OK(RunOpKernel()); 122 123 Tensor expected(allocator(), DT_FLOAT, {3, 4}); 124 expected.flat<float>().setConstant(-2); 125 expected.tensor<float, 2>()(0, 1) = 2; 126 expected.tensor<float, 2>()(0, 2) = 2; 127 expected.tensor<float, 2>()(2, 3) = 2; 128 test::ExpectTensorEqual<float>(expected, *GetOutput(0)); 129 } 130 131 TEST_F(SparseToDenseTest, TwoD_MultValues) { 132 MakeOp(2, DT_INT32, DT_FLOAT); 133 134 // sparse_indices 135 AddInputFromArray<int32>(TensorShape({3, 2}), {0, 1, 0, 2, 2, 3}); 136 // output_shape 137 AddInputFromArray<int32>(TensorShape({2}), {3, 4}); 138 // sparse_values 139 AddInputFromArray<float>(TensorShape({3}), {3, 4, 5}); 140 // default_value 141 AddInputFromArray<float>(TensorShape({}), {-2}); 142 143 TF_ASSERT_OK(RunOpKernel()); 144 145 Tensor expected(allocator(), DT_FLOAT, {3, 4}); 146 expected.flat<float>().setConstant(-2); 147 expected.tensor<float, 2>()(0, 1) = 3; 148 expected.tensor<float, 2>()(0, 2) = 4; 149 expected.tensor<float, 2>()(2, 3) = 5; 150 test::ExpectTensorEqual<float>(expected, *GetOutput(0)); 151 } 152 153 TEST_F(SparseToDenseTest, ThreeD_OneValue) { 154 MakeOp(3, DT_INT32, DT_FLOAT); 155 156 // sparse_indices 157 AddInputFromArray<int32>(TensorShape({3, 3}), {0, 1, 1, 0, 2, 0, 2, 3, 1}); 158 // output_shape 159 AddInputFromArray<int32>(TensorShape({3}), {3, 4, 2}); 160 // sparse_values 161 AddInputFromArray<float>(TensorShape({}), {2}); 162 // default_value 163 AddInputFromArray<float>(TensorShape({}), {-2}); 164 165 TF_ASSERT_OK(RunOpKernel()); 166 167 Tensor expected(allocator(), DT_FLOAT, {3, 4, 2}); 168 expected.flat<float>().setConstant(-2); 169 expected.tensor<float, 3>()(0, 1, 1) = 2; 170 expected.tensor<float, 3>()(0, 2, 0) = 2; 171 expected.tensor<float, 3>()(2, 3, 1) = 2; 172 test::ExpectTensorEqual<float>(expected, *GetOutput(0)); 173 } 174 175 TEST_F(SparseToDenseTest, ThreeD_MultValues) { 176 MakeOp(3, DT_INT32, DT_FLOAT); 177 178 // sparse_indices 179 AddInputFromArray<int32>(TensorShape({3, 3}), {0, 1, 1, 0, 2, 0, 2, 3, 1}); 180 // output_shape 181 AddInputFromArray<int32>(TensorShape({3}), {3, 4, 2}); 182 // sparse_values 183 AddInputFromArray<float>(TensorShape({3}), {3, 4, 5}); 184 // default_value 185 AddInputFromArray<float>(TensorShape({}), {-2}); 186 187 TF_ASSERT_OK(RunOpKernel()); 188 189 Tensor expected(allocator(), DT_FLOAT, {3, 4, 2}); 190 expected.flat<float>().setConstant(-2); 191 expected.tensor<float, 3>()(0, 1, 1) = 3; 192 expected.tensor<float, 3>()(0, 2, 0) = 4; 193 expected.tensor<float, 3>()(2, 3, 1) = 5; 194 test::ExpectTensorEqual<float>(expected, *GetOutput(0)); 195 } 196 197 } // namespace 198 199 static int BM_Arg(int ndim, int n) { return (ndim * 1000000) + n; } 200 static int NDIM_from_arg(int bm_arg) { return bm_arg / 1000000; } 201 static int N_from_arg(int bm_arg) { return bm_arg % 1000000; } 202 203 static void BM_SparseToDense(int iters, const int bm_arg) { 204 const int NDIM = NDIM_from_arg(bm_arg); 205 const int N = N_from_arg(bm_arg); 206 // TODO(zhifengc): Switch to use kernel_benchmark_testlib.h 207 tensorflow::testing::StopTiming(); 208 209 const int IndexDim = (NDIM == 1) ? 0 : 1; 210 211 std::unique_ptr<Device> device( 212 DeviceFactory::NewDevice("CPU", {}, "/job:a/replica:0/task:0")); 213 214 gtl::InlinedVector<TensorValue, 4> inputs; 215 216 // Create a dense tensor with dims [1, ..., 1, N] 217 Tensor output_shape(DT_INT32, TensorShape({NDIM})); 218 Tensor sparse_indices(DT_INT32, TensorShape({N, NDIM})); 219 Tensor sparse_values(DT_FLOAT, TensorShape({N})); 220 Tensor default_value(DT_FLOAT, TensorShape({})); 221 auto output_shape_t = output_shape.vec<int32>(); 222 for (int d = 0; d < NDIM; ++d) { 223 output_shape_t(d) = (d == IndexDim) ? N : 3; 224 } 225 226 auto sparse_indices_t = sparse_indices.matrix<int32>(); 227 for (int n = 0; n < N; ++n) { 228 for (int d = 0; d < NDIM; ++d) 229 sparse_indices_t(n, d) = (d == IndexDim) ? n : 0; 230 } 231 232 for (auto* ptr : 233 {&sparse_indices, &output_shape, &sparse_values, &default_value}) { 234 inputs.push_back({nullptr, ptr}); 235 } 236 237 NodeDef sparse_node_def; 238 TF_CHECK_OK(NodeDefBuilder("sparsetodense", "SparseToDense") 239 .Input(FakeInput(DT_INT32)) 240 .Input(FakeInput(DT_INT32)) 241 .Input(FakeInput(DT_FLOAT)) 242 .Input(FakeInput(DT_FLOAT)) 243 .Finalize(&sparse_node_def)); 244 245 Status status; 246 std::unique_ptr<OpKernel> op(CreateOpKernel(DEVICE_CPU, device.get(), 247 cpu_allocator(), sparse_node_def, 248 TF_GRAPH_DEF_VERSION, &status)); 249 250 OpKernelContext::Params params; 251 params.device = device.get(); 252 params.frame_iter = FrameAndIter(0, 0); 253 params.inputs = &inputs; 254 params.op_kernel = op.get(); 255 std::vector<AllocatorAttributes> attrs; 256 test::SetOutputAttrs(¶ms, &attrs); 257 258 std::unique_ptr<OpKernelContext> sparse_context(new OpKernelContext(¶ms)); 259 op->Compute(sparse_context.get()); 260 tensorflow::testing::StartTiming(); 261 for (int i = 0; i < iters; ++i) { 262 delete sparse_context->release_output(0).tensor; 263 op->Compute(sparse_context.get()); 264 TF_ASSERT_OK(sparse_context->status()); 265 } 266 tensorflow::testing::StopTiming(); 267 268 // processing input, mainly 269 int64 bytes_per_iter = static_cast<int64>((N + N * NDIM) * sizeof(float)); 270 271 tensorflow::testing::BytesProcessed(bytes_per_iter * iters); 272 } 273 274 BENCHMARK(BM_SparseToDense) 275 ->Arg(BM_Arg(1, 10)) 276 ->Arg(BM_Arg(1, 100)) 277 ->Arg(BM_Arg(1, 1000)) 278 ->Arg(BM_Arg(1, 10000)) 279 ->Arg(BM_Arg(2, 10)) 280 ->Arg(BM_Arg(2, 100)) 281 ->Arg(BM_Arg(2, 1000)) 282 ->Arg(BM_Arg(2, 10000)) 283 ->Arg(BM_Arg(3, 10)) 284 ->Arg(BM_Arg(3, 100)) 285 ->Arg(BM_Arg(3, 1000)) 286 ->Arg(BM_Arg(3, 10000)) 287 ->Arg(BM_Arg(5, 10)) 288 ->Arg(BM_Arg(5, 100)) 289 ->Arg(BM_Arg(5, 1000)) 290 ->Arg(BM_Arg(5, 10000)); 291 292 } // namespace tensorflow 293