TensorFlow Serving C++ API Documentation
resource_util.h
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 #ifndef TENSORFLOW_SERVING_RESOURCES_RESOURCE_UTIL_H_
17 #define TENSORFLOW_SERVING_RESOURCES_RESOURCE_UTIL_H_
18 
19 #include <map>
20 #include <string>
21 
22 #include "tensorflow/core/lib/core/status.h"
23 #include "tensorflow/core/platform/macros.h"
24 #include "tensorflow_serving/resources/resources.pb.h"
25 
26 namespace tensorflow {
27 namespace serving {
28 
29 // Arithmetic and comparison operations on resource allocations.
30 //
31 // The implementations assume that the number of devices, and the number of
32 // instances of each device, are both quite small (fewer than, say, 10). Their
33 // computational complexity in these dimensions leaves room for improvement.
34 class ResourceUtil {
35  public:
36  struct Options {
37  // The devices managed by the system, and the number of instances of each.
38  std::map<string, uint32> devices;
39  };
40  explicit ResourceUtil(const Options& options);
41  virtual ~ResourceUtil() = default;
42 
43  // Determines whether 'allocation' is valid, i.e.:
44  // 1. It only refers to valid devices, i.e. those supplied via Options.
45  // 2. Each entry is either unbound, or bound to a valid device instance.
46  // 3. No distinct Resource entry occurs twice, i.e. resource is a key.
47  //
48  // All other methods in this class assume their inputs are valid (i.e. they
49  // have undefined behavior otherwise), and guarantee to produce valid outputs.
50  virtual Status VerifyValidity(const ResourceAllocation& allocation) const;
51 
52  // Determine if the override_allocation's device set is a subset of
53  // base_allocation. Only used by controller
54  Status VerifyOverrideDeviceValidity(
55  const ResourceAllocation& base_allocation,
56  const ResourceAllocation& override_allocation) const;
57 
58  // Verifies whether 'resource' is valid, i.e. it only refers to valid devices,
59  // i.e. those supplied via Options.
60  Status VerifyResourceValidity(const Resource& resource) const;
61 
62  // Converts 'allocation' to normal form, meaning:
63  // 1. It has no entries with quantity 0.
64  // 2. Resources of a device that has exactly one instance are bound to that
65  // instance.
66  virtual ResourceAllocation Normalize(
67  const ResourceAllocation& allocation) const;
68 
69  // Determines whether 'allocation' is in normal form, as defined above.
70  virtual bool IsNormalized(const ResourceAllocation& allocation) const;
71 
72  // Determines whether 'allocation' is bound, defined as follows:
73  // 1. An individual entry is bound iff a device_instance is supplied.
74  // 2. An allocation is bound iff every entry is bound.
75  bool IsBound(const ResourceAllocation& allocation) const;
76 
77  // Creates a bound resource with the given values. For single-instance
78  // resources (which is a common case, e.g. main memory) the 'instance'
79  // argument can be omitted.
80  Resource CreateBoundResource(const string& device, const string& kind,
81  uint32 device_instance = 0) const;
82 
83  // Gets the quantity of 'resource' present in 'allocation'. Returns 0 if
84  // 'resource' is not mentioned in 'allocation', since unmentioned resources
85  // are implicitly zero.
86  uint64_t GetQuantity(const Resource& resource,
87  const ResourceAllocation& allocation) const;
88 
89  // Sets the quantity of 'resource' to 'quantity' in 'allocation', overwriting
90  // any existing quantity.
91  void SetQuantity(const Resource& resource, uint64_t quantity,
92  ResourceAllocation* allocation) const;
93 
94  // Adds 'to_add' to 'base'.
95  //
96  // Keeps bound and unbound entries separate. For example, adding
97  // {(GPU/<no_instance>/RAM/8)} to {(GPU/instance_0/RAM/16),
98  // (GPU/<no_instance>/RAM/4)} yields {(GPU/instance_0/RAM/16),
99  // (GPU/<no_instance>/RAM/12)}.
100  void Add(const ResourceAllocation& to_add, ResourceAllocation* base) const;
101 
102  // Attempts to subtract 'to_subtract' from 'base'. Like Add(), keeps bound and
103  // unbound entries separate. Returns true and mutates 'base' iff the
104  // subtraction is legal, i.e. no negative quantities (which cannot be
105  // represented) are produced.
106  bool Subtract(const ResourceAllocation& to_subtract,
107  ResourceAllocation* base) const;
108 
109  // Multiplies every resource quantity in 'base' by 'multiplier'. Keeps bound
110  // and unbound entries separate.
111  void Multiply(uint64_t multiplier, ResourceAllocation* base) const;
112 
113  // Determines whether two ResourceAllocation objects are identical (modulo
114  // normalization).
115  bool Equal(const ResourceAllocation& lhs,
116  const ResourceAllocation& rhs) const;
117 
118  // Determines whether two Resource objects are identical (modulo
119  // normalization).
120  bool ResourcesEqual(const Resource& lhs, const Resource& rhs) const;
121 
122  // Takes a (bound or unbound) allocation 'lhs' and a *bound* allocation 'rhs'.
123  // Returns true iff for each entry in 'lhs', either:
124  // 1. The entry is bound and its quantity is <= the corresponding one in
125  // 'rhs'.
126  // 2. The entry is unbound, and there exists an instance I of the device s.t.
127  // the unbound quantity in 'lhs' is <= the quantity in 'rhs' bound to I.
128  //
129  // IMPORTANT: Assumes 'rhs' is bound; has undefined behavior otherwise.
130  bool LessThanOrEqual(const ResourceAllocation& lhs,
131  const ResourceAllocation& rhs) const;
132 
133  // Converts a (potentially) unbound allocation into a bound one, by taking
134  // each unbound quantity and binding it to every instance of the device.
135  // (Existing bound quantities are preserved.)
136  //
137  // For example, if there is one CPU and two GPUs then overbinding
138  // {(CPU/instance_0/RAM/16), (GPU/<no_instance>/RAM/4)} yields
139  // {(CPU/instance_0/RAM/16), (GPU/instance_0/RAM/4), (GPU/instance_1/RAM/4)}.
140  //
141  // This operation is useful for reasoning about monotonicity and availability
142  // of resources, not as a means to permanently bind resources to devices
143  // (because it binds resources redundantly to all device instances).
144  ResourceAllocation Overbind(const ResourceAllocation& allocation) const;
145 
146  // Gets the max of the two ResourceAllocations by taking the max of every
147  // paired entry and keep all the unpaired entries in the max. Like the Add()
148  // and Subtract() methods, this keeps the bound and unbound resources
149  // separate.
150  //
151  // E.g.
152  // Max({(GPU/instance_0/RAM/8),
153  // (CPU/instance_0/processing_in_millicores/100)},
154  // {(GPU/instance_0/RAM/16), (CPU/<no_instance>/RAM/4)}) returns
155  // {(GPU/instance_0/RAM/16), (CPU/instance_0/processing_in_millicores/100),
156  // (CPU/<no_instance>/RAM/4)}
157  ResourceAllocation Max(const ResourceAllocation& lhs,
158  const ResourceAllocation& rhs) const;
159 
160  // Gets the min of the two ResourceAllocations by taking the min of every
161  // paired entry and remove all the unpaired entries in the result. Like the
162  // Max() method, this keeps the bound and unbound resources separate.
163  //
164  // E.g.
165  // Min(
166  // {(GPU/instance_0/RAM/8), (CPU/instance_0/processing_in_millicores/100)},
167  // {(GPU/instance_0/RAM/16), (CPU/<no_instance>/RAM/4)}
168  // )
169  // returns
170  // {(GPU/instance_0/RAM/8)}
171  ResourceAllocation Min(const ResourceAllocation& lhs,
172  const ResourceAllocation& rhs) const;
173 
174  // The implementation of ResourceUtil::Normalize().
175  // Converts 'allocation' to normal form, meaning:
176  // 1. It has no entries with quantity 0.
177  // 2. Resources of a device that has exactly one instance are bound to that
178  // instance.
179  ResourceAllocation NormalizeResourceAllocation(
180  const ResourceAllocation& allocation) const;
181 
182  private:
183  enum class DCHECKFailOption { kDoDCHECKFail, kDoNotDCHECKFail };
184 
185  // Wraps fn() with the option to DCHECK-fail.
186  Status VerifyFunctionInternal(std::function<Status()> fn,
187  DCHECKFailOption dcheck_fail_option) const;
188 
189  // Converts 'resource' to normal form, i.e. ensures that if the device has
190  // exactly one instance, the resource is bound to that instance.
191  Resource NormalizeResource(const Resource& resource) const;
192 
193  // Determines whether 'resource' is normalized. Assumes 'resource' is valid.
194  bool IsResourceNormalized(const Resource& resource) const;
195 
196  // Like IsBound(), but assumes the input is normalized.
197  bool IsBoundNormalized(const ResourceAllocation& allocation) const;
198 
199  // Like Add(), but assumes the input is normalized and produces normalized
200  // output.
201  void AddNormalized(const ResourceAllocation& to_add,
202  ResourceAllocation* base) const;
203 
204  // Like Subtract(), but assumes the input is normalized and produces
205  // normalized output.
206  bool SubtractNormalized(const ResourceAllocation& to_subtract,
207  ResourceAllocation* base) const;
208 
209  // Like Multiply(), but assumes the input is normalized and produces
210  // normalized output.
211  void MultiplyNormalized(uint64_t multiplier, ResourceAllocation* base) const;
212 
213  // Like Equal(), but assumes the input is normalized.
214  bool EqualNormalized(const ResourceAllocation& lhs,
215  const ResourceAllocation& rhs) const;
216 
217  // Like ResourcesEqual(), but assumes the input is normalized.
218  bool ResourcesEqualNormalized(const Resource& lhs, const Resource& rhs) const;
219 
220  // Like LessThanOrEqual(), but assumes the input is normalized.
221  bool LessThanOrEqualNormalized(const ResourceAllocation& lhs,
222  const ResourceAllocation& rhs) const;
223 
224  // Like Overbind(), but assumes the input is normalized and produces
225  // normalized output.
226  ResourceAllocation OverbindNormalized(
227  const ResourceAllocation& allocation) const;
228 
229  // Like Max(), but assumes the input are normalized and produces a normalized
230  // result.
231  ResourceAllocation MaxNormalized(const ResourceAllocation& lhs,
232  const ResourceAllocation& rhs) const;
233 
234  // Like Min(), but assumes the input are normalized and produces a normalized
235  // result.
236  ResourceAllocation MinNormalized(const ResourceAllocation& lhs,
237  const ResourceAllocation& rhs) const;
238 
239  // The implementation of ResourceUtil::IsNormalized().
240  bool IsResourceAllocationNormalized(
241  const ResourceAllocation& allocation) const;
242 
243  const std::map<string, uint32> devices_;
244 
245  TF_DISALLOW_COPY_AND_ASSIGN(ResourceUtil);
246 };
247 
248 } // namespace serving
249 } // namespace tensorflow
250 
251 #endif // TENSORFLOW_SERVING_RESOURCES_RESOURCE_UTIL_H_