1 /* Copyright 2017 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 #ifndef TENSORFLOW_CORE_LIB_MONITORING_GAUGE_H_
17 #define TENSORFLOW_CORE_LIB_MONITORING_GAUGE_H_
18
19 // clang-format off
20 // Required for IS_MOBILE_PLATFORM
21 #include "tensorflow/core/platform/platform.h"
22 // clang-format on
23
24 // We replace this implementation with a null implementation for mobile
25 // platforms.
26 #ifdef IS_MOBILE_PLATFORM
27
28 #include <functional>
29 #include <string>
30
31 #include "tensorflow/core/lib/core/status.h"
32 #include "tensorflow/core/platform/macros.h"
33 #include "tensorflow/core/platform/types.h"
34
35 namespace tensorflow {
36 namespace monitoring {
37
38 // GaugeCell which has a null implementation.
39 template <typename T>
40 class GaugeCell {
41 public:
42 public:
GaugeCell()43 GaugeCell() {}
~GaugeCell()44 ~GaugeCell() {}
45
Set(const T & value)46 void Set(const T& value) {}
value()47 T value() const { return T(); }
48
49 private:
50 TF_DISALLOW_COPY_AND_ASSIGN(GaugeCell);
51 };
52
53 // Gauge which has a null implementation.
54 template <typename ValueType, int NumLabels>
55 class Gauge {
56 public:
~Gauge()57 ~Gauge() {}
58
59 template <typename... MetricDefArgs>
New(MetricDefArgs &&...metric_def_args)60 static Gauge* New(MetricDefArgs&&... metric_def_args) {
61 static_assert(
62 std::is_same<ValueType, int64>::value ||
63 std::is_same<ValueType, std::string>::value ||
64 std::is_same<ValueType, bool>::value ||
65 std::is_same<ValueType, std::function<int64()> >::value ||
66 std::is_same<ValueType, std::function<std::string()> >::value ||
67 std::is_same<ValueType, std::function<bool()> >::value,
68 "Gauge only allows bool, int64, and string types.");
69 return new Gauge();
70 }
71
72 template <typename... Labels>
GetCell(const Labels &...labels)73 GaugeCell<ValueType>* GetCell(const Labels&... labels) {
74 return &default_gauge_cell_;
75 }
76
GetStatus()77 Status GetStatus() { return Status::OK(); }
78
79 private:
Gauge()80 Gauge() {}
81
82 GaugeCell<ValueType> default_gauge_cell_;
83
84 TF_DISALLOW_COPY_AND_ASSIGN(Gauge);
85 };
86
87 } // namespace monitoring
88 } // namespace tensorflow
89
90 #else // IS_MOBILE_PLATFORM
91
92 #include <array>
93 #include <atomic>
94 #include <functional>
95 #include <map>
96 #include <string>
97
98 #include "tensorflow/core/lib/core/status.h"
99 #include "tensorflow/core/lib/monitoring/collection_registry.h"
100 #include "tensorflow/core/lib/monitoring/metric_def.h"
101 #include "tensorflow/core/platform/macros.h"
102 #include "tensorflow/core/platform/mutex.h"
103 #include "tensorflow/core/platform/thread_annotations.h"
104 #include "tensorflow/core/platform/types.h"
105
106 namespace tensorflow {
107 namespace monitoring {
108
109 // GaugeCell stores each value of a gauge.
110 //
111 // A cell can be passed off to a module which may repeatedly update it without
112 // needing further map-indexing computations. This improves both encapsulation
113 // (separate modules can own a cell each, without needing to know about the map
114 // to which both cells belong) and performance (since map indexing and
115 // associated locking are both avoided).
116 //
117 // This class is thread-safe.
118 template <typename T>
119 class GaugeCell {
120 public:
GaugeCell(const T & value)121 explicit GaugeCell(const T& value) : value_(value) {}
~GaugeCell()122 ~GaugeCell() {}
123
124 // Atomically sets the value.
125 void Set(const T& value) TF_LOCKS_EXCLUDED(mu_);
126
127 // Retrieves the current value.
128 T value() const TF_LOCKS_EXCLUDED(mu_);
129
130 private:
131 T value_ TF_GUARDED_BY(mu_);
132 mutable mutex mu_;
133
134 TF_DISALLOW_COPY_AND_ASSIGN(GaugeCell);
135 };
136
137 // Explicit specialization of GaugeCell<int64_t>. Compared to the primary
138 // template, it uses atomic values as opposed to mutex. This class is
139 // thread-safe.
140 template <>
141 class GaugeCell<int64_t> {
142 public:
GaugeCell(int64_t value)143 explicit GaugeCell(int64_t value) : value_(value) {}
~GaugeCell()144 ~GaugeCell() {}
145
146 // Atomically sets the value.
147 void Set(int64_t value);
148
149 // Retrieves the current value.
150 int64_t value() const;
151
152 private:
153 std::atomic<int64_t> value_;
154
155 TF_DISALLOW_COPY_AND_ASSIGN(GaugeCell);
156 };
157
158 // Explicit specialization of GaugeCell<bool>. Compared to the primary
159 // template, it uses atomic values as opposed to mutex. This class is
160 // thread-safe.
161 template <>
162 class GaugeCell<bool> {
163 public:
GaugeCell(bool value)164 explicit GaugeCell(bool value) : value_(value) {}
~GaugeCell()165 ~GaugeCell() {}
166
167 // Atomically sets the value.
168 void Set(bool value);
169
170 // Retrieves the current value.
171 bool value() const;
172
173 private:
174 std::atomic<bool> value_;
175
176 TF_DISALLOW_COPY_AND_ASSIGN(GaugeCell);
177 };
178
179 // A stateful class for updating a gauge-like metric. Allowed ValueType are
180 // int64, string and bool.
181 //
182 // This class encapsulates a set of values (or a single value for a label-less
183 // metric). Each value is identified by a tuple of labels. The class allows the
184 // user to set each value.
185 //
186 // Gauge allocates storage and maintains a cell for each value. You can
187 // retrieve an individual cell using a label-tuple and update it separately.
188 // This improves performance since operations related to retrieval, like
189 // map-indexing and locking, are avoided.
190 //
191 // This class is thread-safe.
192 template <typename ValueType, int NumLabels>
193 class Gauge {
194 public:
~Gauge()195 ~Gauge() {
196 // Deleted here, before the metric_def is destroyed.
197 registration_handle_.reset();
198 }
199
200 // Creates the metric based on the metric-definition arguments.
201 //
202 // Example:
203 //
204 // auto* string_gauge_with_label = Gauge<string,1>::New(
205 // "/tensorflow/string_gauge_with_label",
206 // "String gauge with one label.", "MyLabelName");
207 //
208 // auto* integer_gauge = Gauge<int64, 0>::New("/tensorflow/integer_gauge",
209 // "Integer gauge")
210 //
211 // auto* bool_gauge = Gauge<bool, 0>::New("/tensorflow/bool_gauge",
212 // "Bool gauge")
213 template <typename... MetricDefArgs>
214 static Gauge* New(MetricDefArgs&&... metric_def_args);
215
216 // Retrieves the cell for the specified labels, creating it on demand if not
217 // already present.
218 template <typename... Labels>
219 GaugeCell<ValueType>* GetCell(const Labels&... labels) TF_LOCKS_EXCLUDED(mu_);
220
GetStatus()221 Status GetStatus() { return status_; }
222
223 private:
Gauge(const MetricDef<MetricKind::kGauge,ValueType,NumLabels> & metric_def)224 explicit Gauge(
225 const MetricDef<MetricKind::kGauge, ValueType, NumLabels>& metric_def)
226 : metric_def_(metric_def),
227 registration_handle_(CollectionRegistry::Default()->Register(
228 &metric_def_, [&](MetricCollectorGetter getter) {
229 auto metric_collector = getter.Get(&metric_def_);
230
231 mutex_lock l(mu_);
232 for (const auto& cell : cells_) {
233 metric_collector.CollectValue(cell.first, cell.second.value());
234 }
235 })) {
236 if (registration_handle_) {
237 status_ = OkStatus();
238 } else {
239 status_ = Status(tensorflow::error::Code::ALREADY_EXISTS,
240 "Another metric with the same name already exists.");
241 }
242 }
243
244 mutable mutex mu_;
245
246 Status status_;
247
248 // The metric definition. This will be used to identify the metric when we
249 // register it for collection.
250 const MetricDef<MetricKind::kGauge, ValueType, NumLabels> metric_def_;
251
252 std::unique_ptr<CollectionRegistry::RegistrationHandle> registration_handle_;
253
254 using LabelArray = std::array<string, NumLabels>;
255 std::map<LabelArray, GaugeCell<ValueType> > cells_ TF_GUARDED_BY(mu_);
256
257 TF_DISALLOW_COPY_AND_ASSIGN(Gauge);
258 };
259
260 ////
261 // Implementation details follow. API readers may skip.
262 ////
263 template <typename T>
Set(const T & value)264 void GaugeCell<T>::Set(const T& value) {
265 mutex_lock l(mu_);
266 value_ = value;
267 }
268
269 template <typename T>
value()270 T GaugeCell<T>::value() const {
271 mutex_lock l(mu_);
272 return value_;
273 }
274
Set(int64_t value)275 inline void GaugeCell<int64_t>::Set(int64_t value) { value_ = value; }
276
value()277 inline int64_t GaugeCell<int64_t>::value() const { return value_; }
278
Set(bool value)279 inline void GaugeCell<bool>::Set(bool value) { value_ = value; }
280
value()281 inline bool GaugeCell<bool>::value() const { return value_; }
282
283 template <typename ValueType, int NumLabels>
284 template <typename... MetricDefArgs>
New(MetricDefArgs &&...metric_def_args)285 Gauge<ValueType, NumLabels>* Gauge<ValueType, NumLabels>::New(
286 MetricDefArgs&&... metric_def_args) {
287 static_assert(
288 std::is_same<ValueType, int64_t>::value ||
289 std::is_same<ValueType, std::string>::value ||
290 std::is_same<ValueType, bool>::value ||
291 std::is_same<ValueType, std::function<int64_t()> >::value ||
292 std::is_same<ValueType, std::function<std::string()> >::value ||
293 std::is_same<ValueType, std::function<bool()> >::value,
294 "Gauge only allows bool, int64, and string types.");
295 return new Gauge<ValueType, NumLabels>(
296 MetricDef<MetricKind::kGauge, ValueType, NumLabels>(
297 std::forward<MetricDefArgs>(metric_def_args)...));
298 }
299
300 template <typename ValueType, int NumLabels>
301 template <typename... Labels>
GetCell(const Labels &...labels)302 GaugeCell<ValueType>* Gauge<ValueType, NumLabels>::GetCell(
303 const Labels&... labels) TF_LOCKS_EXCLUDED(mu_) {
304 // Provides a more informative error message than the one during array
305 // construction below.
306 static_assert(
307 sizeof...(Labels) == NumLabels,
308 "Mismatch between Gauge<ValueType, NumLabels> and number of labels "
309 "provided in GetCell(...).");
310
311 const LabelArray& label_array = {{labels...}};
312 mutex_lock l(mu_);
313 const auto found_it = cells_.find(label_array);
314 if (found_it != cells_.end()) {
315 return &(found_it->second);
316 }
317 return &(cells_
318 .emplace(std::piecewise_construct,
319 std::forward_as_tuple(label_array),
320 std::forward_as_tuple(ValueType()))
321 .first->second);
322 }
323
324 } // namespace monitoring
325 } // namespace tensorflow
326
327 #endif // IS_MOBILE_PLATFORM
328 #endif // TENSORFLOW_CORE_LIB_MONITORING_GAUGE_H_
329