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