xref: /aosp_15_r20/external/tensorflow/tensorflow/core/lib/monitoring/gauge.h (revision b6fb3261f9314811a0f4371741dbb8839866f948)
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