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"""TensorFlow monitoring APIs.""" 16 17from __future__ import absolute_import 18from __future__ import division 19from __future__ import print_function 20 21import collections 22import functools 23import time 24 25from tensorflow.core.framework import summary_pb2 26from tensorflow.python import pywrap_tfe 27from tensorflow.python.client import pywrap_tf_session 28from tensorflow.python.framework import c_api_util 29from tensorflow.python.util import compat 30from tensorflow.python.util.tf_export import tf_export 31 32_MetricMethod = collections.namedtuple('MetricMethod', 'create delete get_cell') 33_counter_methods = [ 34 _MetricMethod( 35 create=pywrap_tfe.TFE_MonitoringNewCounter0, 36 delete=pywrap_tfe.TFE_MonitoringDeleteCounter0, 37 get_cell=pywrap_tfe.TFE_MonitoringGetCellCounter0), 38 _MetricMethod( 39 create=pywrap_tfe.TFE_MonitoringNewCounter1, 40 delete=pywrap_tfe.TFE_MonitoringDeleteCounter1, 41 get_cell=pywrap_tfe.TFE_MonitoringGetCellCounter1), 42 _MetricMethod( 43 create=pywrap_tfe.TFE_MonitoringNewCounter2, 44 delete=pywrap_tfe.TFE_MonitoringDeleteCounter2, 45 get_cell=pywrap_tfe.TFE_MonitoringGetCellCounter2), 46] 47_int_gauge_methods = [ 48 _MetricMethod( 49 create=pywrap_tfe.TFE_MonitoringNewIntGauge0, 50 delete=pywrap_tfe.TFE_MonitoringDeleteIntGauge0, 51 get_cell=pywrap_tfe.TFE_MonitoringGetCellIntGauge0), 52 _MetricMethod( 53 create=pywrap_tfe.TFE_MonitoringNewIntGauge1, 54 delete=pywrap_tfe.TFE_MonitoringDeleteIntGauge1, 55 get_cell=pywrap_tfe.TFE_MonitoringGetCellIntGauge1), 56 _MetricMethod( 57 create=pywrap_tfe.TFE_MonitoringNewIntGauge2, 58 delete=pywrap_tfe.TFE_MonitoringDeleteIntGauge2, 59 get_cell=pywrap_tfe.TFE_MonitoringGetCellIntGauge2), 60] 61_string_gauge_methods = [ 62 _MetricMethod( 63 create=pywrap_tfe.TFE_MonitoringNewStringGauge0, 64 delete=pywrap_tfe.TFE_MonitoringDeleteStringGauge0, 65 get_cell=pywrap_tfe.TFE_MonitoringGetCellStringGauge0), 66 _MetricMethod( 67 create=pywrap_tfe.TFE_MonitoringNewStringGauge1, 68 delete=pywrap_tfe.TFE_MonitoringDeleteStringGauge1, 69 get_cell=pywrap_tfe.TFE_MonitoringGetCellStringGauge1), 70 _MetricMethod( 71 create=pywrap_tfe.TFE_MonitoringNewStringGauge2, 72 delete=pywrap_tfe.TFE_MonitoringDeleteStringGauge2, 73 get_cell=pywrap_tfe.TFE_MonitoringGetCellStringGauge2), 74 _MetricMethod( 75 create=pywrap_tfe.TFE_MonitoringNewStringGauge3, 76 delete=pywrap_tfe.TFE_MonitoringDeleteStringGauge3, 77 get_cell=pywrap_tfe.TFE_MonitoringGetCellStringGauge3), 78 _MetricMethod( 79 create=pywrap_tfe.TFE_MonitoringNewStringGauge4, 80 delete=pywrap_tfe.TFE_MonitoringDeleteStringGauge4, 81 get_cell=pywrap_tfe.TFE_MonitoringGetCellStringGauge4), 82] 83_bool_gauge_methods = [ 84 _MetricMethod( 85 create=pywrap_tfe.TFE_MonitoringNewBoolGauge0, 86 delete=pywrap_tfe.TFE_MonitoringDeleteBoolGauge0, 87 get_cell=pywrap_tfe.TFE_MonitoringGetCellBoolGauge0), 88 _MetricMethod( 89 create=pywrap_tfe.TFE_MonitoringNewBoolGauge1, 90 delete=pywrap_tfe.TFE_MonitoringDeleteBoolGauge1, 91 get_cell=pywrap_tfe.TFE_MonitoringGetCellBoolGauge1), 92 _MetricMethod( 93 create=pywrap_tfe.TFE_MonitoringNewBoolGauge2, 94 delete=pywrap_tfe.TFE_MonitoringDeleteBoolGauge2, 95 get_cell=pywrap_tfe.TFE_MonitoringGetCellBoolGauge2), 96] 97_sampler_methods = [ 98 _MetricMethod( 99 create=pywrap_tfe.TFE_MonitoringNewSampler0, 100 delete=pywrap_tfe.TFE_MonitoringDeleteSampler0, 101 get_cell=pywrap_tfe.TFE_MonitoringGetCellSampler0), 102 _MetricMethod( 103 create=pywrap_tfe.TFE_MonitoringNewSampler1, 104 delete=pywrap_tfe.TFE_MonitoringDeleteSampler1, 105 get_cell=pywrap_tfe.TFE_MonitoringGetCellSampler1), 106 _MetricMethod( 107 create=pywrap_tfe.TFE_MonitoringNewSampler2, 108 delete=pywrap_tfe.TFE_MonitoringDeleteSampler2, 109 get_cell=pywrap_tfe.TFE_MonitoringGetCellSampler2), 110] 111 112 113class Metric(object): 114 """The base class of metric.""" 115 116 __slots__ = ["_metric", "_metric_name", "_metric_methods", "_label_length"] 117 118 def __init__(self, metric_name, metric_methods, label_length, *args): 119 """Creates a new metric. 120 121 Args: 122 metric_name: name of the metric class. 123 metric_methods: list of swig metric methods. 124 label_length: length of label args. 125 *args: the arguments to call create method. 126 """ 127 self._metric_name = metric_name 128 self._metric_methods = metric_methods 129 self._label_length = label_length 130 131 if label_length >= len(self._metric_methods): 132 raise ValueError('Cannot create {} metric with label >= {}'.format( 133 self._metric_name, len(self._metric_methods))) 134 135 self._metric = self._metric_methods[self._label_length].create(*args) 136 137 def __del__(self): 138 try: 139 deleter = self._metric_methods[self._label_length].delete 140 metric = self._metric 141 except AttributeError: 142 return 143 144 if deleter is not None: 145 deleter(metric) 146 147 def get_cell(self, *labels): 148 """Retrieves the cell.""" 149 if len(labels) != self._label_length: 150 raise ValueError('The {} expects taking {} labels'.format( 151 self._metric_name, self._label_length)) 152 return self._metric_methods[self._label_length].get_cell( 153 self._metric, *labels) 154 155 156class CounterCell(object): 157 """CounterCell stores each value of a Counter.""" 158 159 __slots__ = ["_cell"] 160 161 def __init__(self, cell): 162 """Creates a new CounterCell. 163 164 Args: 165 cell: A c pointer of TFE_MonitoringCounterCell. 166 """ 167 self._cell = cell 168 169 def increase_by(self, value): 170 """Atomically increments the value. 171 172 Args: 173 value: non-negative value. 174 """ 175 pywrap_tfe.TFE_MonitoringCounterCellIncrementBy(self._cell, value) 176 177 def value(self): 178 """Retrieves the current value.""" 179 return pywrap_tfe.TFE_MonitoringCounterCellValue(self._cell) 180 181 182class Counter(Metric): 183 """A stateful class for updating a cumulative integer metric. 184 185 This class encapsulates a set of values (or a single value for a label-less 186 metric). Each value is identified by a tuple of labels. The class allows the 187 user to increment each value. 188 """ 189 190 __slots__ = [] 191 192 def __init__(self, name, description, *labels): 193 """Creates a new Counter. 194 195 Args: 196 name: name of the new metric. 197 description: description of the new metric. 198 *labels: The label list of the new metric. 199 """ 200 super(Counter, self).__init__('Counter', _counter_methods, len(labels), 201 name, description, *labels) 202 203 def get_cell(self, *labels): 204 """Retrieves the cell.""" 205 return CounterCell(super(Counter, self).get_cell(*labels)) 206 207 208class IntGaugeCell(object): 209 """A single integer value stored in an `IntGauge`.""" 210 211 __slots__ = ["_cell"] 212 213 def __init__(self, cell): 214 """Creates a new IntGaugeCell. 215 216 Args: 217 cell: A c pointer of TFE_MonitoringIntGaugeCell. 218 """ 219 self._cell = cell 220 221 def set(self, value): 222 """Atomically set the value. 223 224 Args: 225 value: integer value. 226 """ 227 pywrap_tfe.TFE_MonitoringIntGaugeCellSet(self._cell, value) 228 229 def value(self): 230 """Retrieves the current value.""" 231 return pywrap_tfe.TFE_MonitoringIntGaugeCellValue(self._cell) 232 233 234class IntGauge(Metric): 235 """A stateful class for updating a gauge-like integer metric. 236 237 This class encapsulates a set of integer values (or a single value for a 238 label-less metric). Each value is identified by a tuple of labels. The class 239 allows the user to set each value. 240 """ 241 242 __slots__ = [] 243 244 def __init__(self, name, description, *labels): 245 """Creates a new IntGauge. 246 247 Args: 248 name: name of the new metric. 249 description: description of the new metric. 250 *labels: The label list of the new metric. 251 """ 252 super(IntGauge, self).__init__('IntGauge', _int_gauge_methods, len(labels), 253 name, description, *labels) 254 255 def get_cell(self, *labels): 256 """Retrieves the cell.""" 257 return IntGaugeCell(super(IntGauge, self).get_cell(*labels)) 258 259 260class StringGaugeCell(object): 261 """A single string value stored in an `StringGauge`.""" 262 263 __slots__ = ["_cell"] 264 265 def __init__(self, cell): 266 """Creates a new StringGaugeCell. 267 268 Args: 269 cell: A c pointer of TFE_MonitoringStringGaugeCell. 270 """ 271 self._cell = cell 272 273 def set(self, value): 274 """Atomically set the value. 275 276 Args: 277 value: string value. 278 """ 279 pywrap_tfe.TFE_MonitoringStringGaugeCellSet(self._cell, value) 280 281 def value(self): 282 """Retrieves the current value.""" 283 with c_api_util.tf_buffer() as buffer_: 284 pywrap_tfe.TFE_MonitoringStringGaugeCellValue(self._cell, buffer_) 285 value = pywrap_tf_session.TF_GetBuffer(buffer_).decode('utf-8') 286 return value 287 288 289class StringGauge(Metric): 290 """A stateful class for updating a gauge-like string metric. 291 292 This class encapsulates a set of string values (or a single value for a 293 label-less metric). Each value is identified by a tuple of labels. The class 294 allows the user to set each value. 295 """ 296 297 __slots__ = [] 298 299 def __init__(self, name, description, *labels): 300 """Creates a new StringGauge. 301 302 Args: 303 name: name of the new metric. 304 description: description of the new metric. 305 *labels: The label list of the new metric. 306 """ 307 super(StringGauge, self).__init__('StringGauge', _string_gauge_methods, 308 len(labels), name, description, *labels) 309 310 def get_cell(self, *labels): 311 """Retrieves the cell.""" 312 return StringGaugeCell(super(StringGauge, self).get_cell(*labels)) 313 314 315class BoolGaugeCell(object): 316 """A single boolean value stored in an `BoolGauge`.""" 317 318 __slots__ = ["_cell"] 319 320 def __init__(self, cell): 321 """Creates a new BoolGaugeCell. 322 323 Args: 324 cell: A c pointer of TFE_MonitoringBoolGaugeCell. 325 """ 326 self._cell = cell 327 328 def set(self, value): 329 """Atomically set the value. 330 331 Args: 332 value: bool value. 333 """ 334 pywrap_tfe.TFE_MonitoringBoolGaugeCellSet(self._cell, value) 335 336 def value(self): 337 """Retrieves the current value.""" 338 return pywrap_tfe.TFE_MonitoringBoolGaugeCellValue(self._cell) 339 340 341@tf_export("__internal__.monitoring.BoolGauge", v1=[]) 342class BoolGauge(Metric): 343 """A stateful class for updating a gauge-like bool metric. 344 345 This class encapsulates a set of boolean values (or a single value for a 346 label-less metric). Each value is identified by a tuple of labels. The class 347 allows the user to set each value. 348 """ 349 350 __slots__ = [] 351 352 def __init__(self, name, description, *labels): 353 """Creates a new BoolGauge. 354 355 Args: 356 name: name of the new metric. 357 description: description of the new metric. 358 *labels: The label list of the new metric. 359 """ 360 super(BoolGauge, self).__init__('BoolGauge', _bool_gauge_methods, 361 len(labels), name, description, *labels) 362 363 def get_cell(self, *labels): 364 """Retrieves the cell.""" 365 return BoolGaugeCell(super(BoolGauge, self).get_cell(*labels)) 366 367 368class SamplerCell(object): 369 """SamplerCell stores each value of a Sampler.""" 370 371 __slots__ = ["_cell"] 372 373 def __init__(self, cell): 374 """Creates a new SamplerCell. 375 376 Args: 377 cell: A c pointer of TFE_MonitoringSamplerCell. 378 """ 379 self._cell = cell 380 381 def add(self, value): 382 """Atomically add a sample. 383 384 Args: 385 value: float value. 386 """ 387 pywrap_tfe.TFE_MonitoringSamplerCellAdd(self._cell, value) 388 389 def value(self): 390 """Retrieves the current distribution of samples. 391 392 Returns: 393 A HistogramProto describing the distribution of samples. 394 """ 395 with c_api_util.tf_buffer() as buffer_: 396 pywrap_tfe.TFE_MonitoringSamplerCellValue(self._cell, buffer_) 397 proto_data = pywrap_tf_session.TF_GetBuffer(buffer_) 398 histogram_proto = summary_pb2.HistogramProto() 399 histogram_proto.ParseFromString(compat.as_bytes(proto_data)) 400 return histogram_proto 401 402 403class Buckets(object): 404 """Bucketing strategies for the samplers.""" 405 406 __slots__ = ["buckets"] 407 408 def __init__(self, buckets): 409 """Creates a new Buckets. 410 411 Args: 412 buckets: A c pointer of TFE_MonitoringBuckets. 413 """ 414 self.buckets = buckets 415 416 def __del__(self): 417 pywrap_tfe.TFE_MonitoringDeleteBuckets(self.buckets) 418 419 420class ExponentialBuckets(Buckets): 421 """Exponential bucketing strategy. 422 423 Sets up buckets of the form: 424 [-DBL_MAX, ..., scale * growth^i, 425 scale * growth_factor^(i + 1), ..., DBL_MAX]. 426 """ 427 428 __slots__ = [] 429 430 def __init__(self, scale, growth_factor, bucket_count): 431 """Creates a new exponential Buckets. 432 433 Args: 434 scale: float 435 growth_factor: float 436 bucket_count: integer 437 """ 438 super(ExponentialBuckets, self).__init__( 439 pywrap_tfe.TFE_MonitoringNewExponentialBuckets(scale, growth_factor, 440 bucket_count)) 441 442 443class Sampler(Metric): 444 """A stateful class for updating a cumulative histogram metric. 445 446 This class encapsulates a set of histograms (or a single histogram for a 447 label-less metric) configured with a list of increasing bucket boundaries. 448 Each histogram is identified by a tuple of labels. The class allows the 449 user to add a sample to each histogram value. 450 """ 451 452 __slots__ = [] 453 454 def __init__(self, name, buckets, description, *labels): 455 """Creates a new Sampler. 456 457 Args: 458 name: name of the new metric. 459 buckets: bucketing strategy of the new metric. 460 description: description of the new metric. 461 *labels: The label list of the new metric. 462 """ 463 super(Sampler, self).__init__('Sampler', _sampler_methods, len(labels), 464 name, buckets.buckets, description, *labels) 465 466 def get_cell(self, *labels): 467 """Retrieves the cell.""" 468 return SamplerCell(super(Sampler, self).get_cell(*labels)) 469 470 471class MonitoredTimer(object): 472 """A context manager to measure the walltime and increment a Counter cell.""" 473 474 __slots__ = ["cell", "t"] 475 476 def __init__(self, cell): 477 """Creates a new MonitoredTimer. 478 479 Args: 480 cell: the cell associated with the time metric that will be inremented. 481 """ 482 self.cell = cell 483 484 def __enter__(self): 485 self.t = time.time() 486 return self 487 488 def __exit__(self, exception_type, exception_value, traceback): 489 del exception_type, exception_value, traceback 490 micro_seconds = (time.time() - self.t) * 1000000 491 self.cell.increase_by(int(micro_seconds)) 492 493 494def monitored_timer(cell): 495 """A function decorator for adding MonitoredTimer support. 496 497 Args: 498 cell: the cell associated with the time metric that will be inremented. 499 Returns: 500 A decorator that measure the function runtime and increment the specified 501 counter cell. 502 """ 503 504 def actual_decorator(func): 505 506 @functools.wraps(func) 507 def wrapper(*args, **kwargs): 508 with MonitoredTimer(cell): 509 return func(*args, **kwargs) 510 511 return wrapper 512 513 return actual_decorator 514