TensorFlow Serving C++ API Documentation
batching_util.h
1 /* Copyright 2017 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 #ifndef TENSORFLOW_SERVING_BATCHING_BATCHING_UTIL_H_
17 #define TENSORFLOW_SERVING_BATCHING_BATCHING_UTIL_H_
18 
19 #include <string>
20 #include <utility>
21 #include <vector>
22 
23 #include "absl/strings/str_cat.h"
24 #include "absl/types/span.h"
25 #include "tensorflow/core/framework/tensor.h"
26 #include "tensorflow/core/lib/monitoring/sampler.h"
27 
28 namespace tensorflow {
29 namespace serving {
30 
31 // For batch of inputs calculates maximum dim sizes across all tensors
32 // with the same name.
33 // These dim sizes are used later to calculate padding amount for each tensor.
34 // For example, for batch containing three tasks with the following inputs
35 // (instead of tensors there are their shapes):
36 //
37 // task1: {'tensor_a': [100, 500, 300], 'tensor_b': [100]}
38 // task2: {'tensor_a': [100, 200, 123], 'tensor_b': [100]}
39 // task3: {'tensor_a': [200, 100, 400], 'tensor_b': [200]}
40 //
41 // the following map will be generated:
42 // {'tensor_a': [200, 500, 400], 'tensor_b': [200]}
43 std::map<string, std::vector<int>> CalculateMaxDimSizes(
44  const std::vector<std::vector<std::pair<string, Tensor>>>& batch);
45 
46 // Pads tensor so that its shape becomes as specified in max_dim_sizes,
47 // except for zeroth dimension, which is left as is.
48 // First entry in max_dim_sizes is ignored.
49 // First element of a tensor is used as padding value.
50 // If tensor is empty, an error will be returned.
51 //
52 // For example given input tensor with shape [1, 2, 3, 4] and max_dim_sizes
53 // [1, 2, 5, 8] function produces padded_tensor of shape
54 // [1, 2, 5, 8], padded with tensor[0][0][0][0] element.
55 //
56 // Supported tensor datatypes:
57 // DT_FLOAT, DT_DOUBLE, DT_INT8, DT_UINT8, DT_INT16,
58 // DT_UINT16, DT_INT32, DT_INT64, DT_COMPLEX64, DT_COMPLEX128,
59 // DT_STRING, DT_BOOL, DT_QINT8, DT_QUINT8, DT_QINT16,
60 // DT_QUINT16, DT_QINT32, DT_HALF, DT_RESOURCE.
61 //
62 // Supported tensor ranks: from 1 to 6.
63 
64 Status AddPadding(const Tensor& tensor, absl::Span<const int> max_dim_sizes,
65  Tensor* padded_tensor);
66 
67 // Returns the smallest entry in `allowed_batch_sizes` that is greater than or
68 // equal to `batch_size`. If `allowed_batch_sizes` is empty, simply returns
69 // `batch_size`.
70 int RoundToLowestAllowedBatchSize(absl::Span<const int> allowed_batch_sizes,
71  int batch_size);
72 
73 // Returns true iff all dims of shape1 are equal to dims of shape2 starting with
74 // the first (not zeroth) dimension.
75 // For example, for shapes [1, 2, 3] and [4, 2, 3] the result is true.
76 bool AreShapesEqualExceptZeroDim(const TensorShape& shape1,
77  const TensorShape& shape2);
78 
79 // Returns the first dimension size (batching dimension) of each tensor in
80 // `inputs`. If their first dimension sizes don't match, returns an error.
81 template <typename TensorList, typename DimFunc, typename DimSizeFunc>
82 Status ComputeTensorBatchSize(TensorList inputs, size_t* size, DimFunc dim_func,
83  DimSizeFunc dim_size_func) {
84  if (inputs.empty()) {
85  return errors::InvalidArgument(
86  "Batching Run() must have at least one input tensor");
87  }
88 
89  bool first = true;
90  for (const auto& tensor : inputs) {
91  if (dim_func(tensor) == 0) {
92  return errors::InvalidArgument(
93  "Batching Run() input tensors must have at least one "
94  "dimension");
95  }
96  const size_t this_size = dim_size_func(tensor, 0);
97 
98  if (first) {
99  *size = this_size;
100  first = false;
101  } else {
102  if (this_size != *size) {
103  return errors::InvalidArgument(
104  "Batching Run() input tensors must have equal "
105  "0th-dimension size");
106  }
107  }
108  }
109  return Status();
110 }
111 
112 /***************** Below utilities are for monitoring purpose *****************/
113 
114 // For all metrics: consider adding breakdowns based on model name or status if
115 // needed. Note that model name is not available as a session property or on any
116 // of the inputs currently.
117 template <typename BatchingTask>
118 void RecordPaddingSize(int32 padding_size, int32 execution_batch_size) {
119  static const std::string batching_task_name = BatchingTask::Name();
120  static auto* cell = tensorflow::monitoring::Sampler<1>::New(
121  {absl::StrCat("/tensorflow/serving/", batching_task_name,
122  "/padding_size"),
123  "Tracks the padding size distribution on batches.",
124  "execution_batch_size"},
125  // Exponential buckets [1*2^0, ..., 1*2^13, DBL_MAX].
126  monitoring::Buckets::Exponential(1, 2, 14));
127  cell->GetCell(absl::StrCat(execution_batch_size))
128  ->Add(static_cast<double>(padding_size));
129 }
130 
131 template <typename BatchingTask>
132 void RecordInputBatchSize(int32 batch_size) {
133  static const std::string batching_task_name = BatchingTask::Name();
134  static auto* cell = tensorflow::monitoring::Sampler<0>::New(
135  {absl::StrCat("/tensorflow/serving/", batching_task_name,
136  "/input_batch_size"),
137  "Tracks the batch size distribution on the inputs."},
138  // Exponential buckets [1*2^0, ..., 1*2^13, DBL_MAX].
139  monitoring::Buckets::Exponential(1, 2, 14));
140  cell->GetCell()->Add(static_cast<double>(batch_size));
141 }
142 
143 template <typename BatchingTask>
144 void RecordProcessedBatchSize(int32 batch_size) {
145  static const std::string batching_task_name = BatchingTask::Name();
146  static auto* cell = tensorflow::monitoring::Sampler<0>::New(
147  {absl::StrCat("/tensorflow/serving/", batching_task_name,
148  "/processed_batch_size"),
149  "Tracks the batch size distribution on processing."},
150  // Exponential buckets [1*2^0, ..., 1*2^13, DBL_MAX].
151  monitoring::Buckets::Exponential(1, 2, 14));
152  cell->GetCell()->Add(static_cast<double>(batch_size));
153 }
154 
155 } // namespace serving
156 } // namespace tensorflow
157 #endif // TENSORFLOW_SERVING_BATCHING_BATCHING_UTIL_H_