TensorFlow Serving C++ API Documentation
bundle_factory_test_util.cc
1 /* Copyright 2016 Google Inc. 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 "tensorflow_serving/servables/tensorflow/bundle_factory_test_util.h"
17 
18 #include <memory>
19 #include <queue>
20 #include <string>
21 #include <utility>
22 #include <vector>
23 
24 #include "tensorflow/cc/saved_model/constants.h"
25 #include "tensorflow/core/framework/tensor.h"
26 #include "tensorflow/core/framework/tensor_testutil.h"
27 #include "tensorflow/core/lib/core/status.h"
28 #include "tensorflow/core/lib/core/status_test_util.h"
29 #include "tensorflow/core/lib/io/path.h"
30 #include "tensorflow/core/platform/env.h"
31 #include "tensorflow/core/platform/threadpool_options.h"
32 #include "tensorflow_serving/resources/resource_values.h"
33 #include "tensorflow_serving/test_util/test_util.h"
34 
35 namespace tensorflow {
36 namespace serving {
37 namespace test_util {
38 
39 namespace {
40 
41 const char kTestSavedModelPath[] =
42  "cc/saved_model/testdata/half_plus_two/00000123";
43 const char kTestSessionBundleExportPath[] =
44  "session_bundle/testdata/half_plus_two/00000123";
45 const char kTestTfLiteModelPath[] =
46  "servables/tensorflow/testdata/saved_model_half_plus_two_tflite/00000123";
47 const char kTestMLMDSavedModelPath[] =
48  "servables/tensorflow/testdata/half_plus_two_mlmd/00000123";
49 
50 } // namespace
51 
52 string GetTestSavedModelPath() {
53  return test_util::TensorflowTestSrcDirPath(kTestSavedModelPath);
54 }
55 
56 string GetTestMLMetadataSavedModelPath() {
57  return test_util::TensorflowTestSrcDirPath(kTestMLMDSavedModelPath);
58 }
59 
60 string GetTestSessionBundleExportPath() {
61  return test_util::TestSrcDirPath(kTestSessionBundleExportPath);
62 }
63 
64 string GetTestTfLiteModelPath() {
65  return test_util::TestSrcDirPath(kTestTfLiteModelPath);
66 }
67 
68 std::vector<string> GetTestSessionBundleExportFiles() {
69  const string dir = GetTestSessionBundleExportPath();
70  return {tensorflow::io::JoinPath(dir, "export.meta"),
71  tensorflow::io::JoinPath(dir, "export-00000-of-00001")};
72 }
73 
74 std::vector<string> GetTestSavedModelBundleExportFiles() {
75  const string dir = GetTestSavedModelPath();
76  return {
77  tensorflow::io::JoinPath(dir, "saved_model.pb"),
78  tensorflow::io::JoinPath(dir, "assets/foo.txt"),
79  tensorflow::io::JoinPath(dir, "variables/variables.index"),
80  tensorflow::io::JoinPath(dir, "variables/variables.data-00000-of-00001")};
81 }
82 
83 uint64_t GetTotalFileSize(const std::vector<string>& files) {
84  uint64_t total_file_size = 0;
85  for (const string& file : files) {
86  if (!(Env::Default()->IsDirectory(file).ok()) &&
87  Env::Default()->FileExists(file).ok()) {
88  uint64_t file_size;
89  TF_CHECK_OK(Env::Default()->GetFileSize(file, &file_size));
90  total_file_size += file_size;
91  }
92  }
93  return total_file_size;
94 }
95 
96 SignatureDef GetTestSessionSignature() {
97  SignatureDef signature;
98  TensorInfo input;
99  input.set_name("x:0");
100  (*signature.mutable_inputs())["x"] = input;
101  TensorInfo output;
102  output.set_name("y:0");
103  (*signature.mutable_outputs())["y"] = output;
104  return signature;
105 }
106 
107 void TestSingleRequest(Session* session, int input_batch_size) {
108  Tensor input(DT_FLOAT, TensorShape({input_batch_size}));
109  test::FillIota<float>(&input, 100.0f);
110  // half plus two: output should be input / 2 + 2.
111  Tensor expected_output(DT_FLOAT, TensorShape({input_batch_size}));
112  test::FillFn<float>(&expected_output,
113  [](int i) -> float { return (100.0f + i) / 2 + 2; });
114 
115  // Note that "x" and "y" are the actual names of the nodes in the graph.
116  // The saved manifest binds these to "input" and "output" respectively, but
117  // these tests are focused on the raw underlying session without bindings.
118  const std::vector<std::pair<string, Tensor>> inputs = {{"x:0", input}};
119  const std::vector<string> output_names = {"y:0"};
120  const std::vector<string> empty_targets;
121  std::vector<Tensor> outputs;
122 
123  RunMetadata run_metadata;
124  TF_ASSERT_OK(session->Run(RunOptions{}, inputs, output_names, empty_targets,
125  &outputs, &run_metadata,
126  thread::ThreadPoolOptions{}));
127 
128  ASSERT_EQ(1, outputs.size());
129  const auto& single_output = outputs.at(0);
130  test::ExpectTensorEqual<float>(expected_output, single_output);
131 }
132 
133 void TestMultipleRequests(Session* session, int num_requests,
134  int input_batch_size) {
135  std::vector<std::unique_ptr<Thread>> request_threads;
136  request_threads.reserve(num_requests);
137  for (int i = 0; i < num_requests; ++i) {
138  request_threads.push_back(
139  std::unique_ptr<Thread>(Env::Default()->StartThread(
140  ThreadOptions(), strings::StrCat("thread_", i),
141  [session, input_batch_size] {
142  TestSingleRequest(session, input_batch_size);
143  })));
144  }
145 }
146 
147 ResourceAllocation GetExpectedResourceEstimate(double total_file_size) {
148  // kResourceEstimateRAMMultiplier and kResourceEstimateRAMPadBytes should
149  // match the constants defined in bundle_factory_util.cc.
150  const double kResourceEstimateRAMMultiplier = 1.2;
151  const int kResourceEstimateRAMPadBytes = 0;
152  const uint64_t expected_ram_requirement =
153  total_file_size * kResourceEstimateRAMMultiplier +
154  kResourceEstimateRAMPadBytes;
155  ResourceAllocation resource_alloc;
156  ResourceAllocation::Entry* ram_entry =
157  resource_alloc.add_resource_quantities();
158  Resource* ram_resource = ram_entry->mutable_resource();
159  ram_resource->set_device(device_types::kMain);
160  ram_resource->set_kind(resource_kinds::kRamBytes);
161  ram_entry->set_quantity(expected_ram_requirement);
162  return resource_alloc;
163 }
164 
165 void CopyDirOrDie(const string& src_dir, const string& dst_dir) {
166  int64_t u_files = 0;
167  int64_t u_dirs = 0;
168  if (Env::Default()->IsDirectory(dst_dir).ok()) {
169  TF_ASSERT_OK(Env::Default()->DeleteRecursively(dst_dir, &u_files, &u_dirs));
170  }
171  TF_ASSERT_OK(Env::Default()->RecursivelyCreateDir(dst_dir));
172  std::queue<std::string> dirs_to_copy;
173  dirs_to_copy.push(src_dir);
174  while (!dirs_to_copy.empty()) {
175  const string dir = dirs_to_copy.front();
176  dirs_to_copy.pop();
177  std::vector<std::string> children;
178  TF_ASSERT_OK(Env::Default()->GetChildren(dir, &children));
179  for (const string& child : children) {
180  const string child_path = io::JoinPath(dir, child);
181  StringPiece remainder = child_path;
182  CHECK(str_util::ConsumePrefix(&remainder, src_dir));
183  if (Env::Default()->IsDirectory(child_path).ok()) {
184  TF_ASSERT_OK(
185  Env::Default()->CreateDir(io::JoinPath(dst_dir, remainder)));
186  dirs_to_copy.push(child_path);
187  } else {
188  TF_ASSERT_OK(Env::Default()->CopyFile(
189  child_path, io::JoinPath(dst_dir, remainder)));
190  }
191  }
192  }
193 }
194 
195 } // namespace test_util
196 } // namespace serving
197 } // namespace tensorflow