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"""State management for eager execution.""" 16 17from __future__ import absolute_import 18from __future__ import division 19from __future__ import print_function 20 21import collections 22import contextlib 23import copy 24import random 25import threading 26 27from absl import logging 28import numpy as np 29import six 30 31from tensorflow.core.protobuf import config_pb2 32from tensorflow.core.protobuf import rewriter_config_pb2 33from tensorflow.python import pywrap_tfe 34from tensorflow.python import tf2 35from tensorflow.python.client import pywrap_tf_session 36from tensorflow.python.eager import executor 37from tensorflow.python.eager import monitoring 38from tensorflow.python.framework import c_api_util 39from tensorflow.python.framework import device as pydev 40from tensorflow.python.util import compat 41from tensorflow.python.util import is_in_graph_mode 42from tensorflow.python.util import tf_contextlib 43from tensorflow.python.util.tf_export import tf_export 44 45GRAPH_MODE = 0 46EAGER_MODE = 1 47 48default_execution_mode = EAGER_MODE if tf2.enabled() else GRAPH_MODE 49 50# Cache from (old_device_name, partial_new_device_name) -> (new_device_name, 51# new_device_spec). 52# Note that we do not protect this with a lock and instead rely on python's GIL 53# and the idempotent nature of writes to provide thread safety. 54_device_parsing_cache = {} 55_starting_device_spec = pydev.DeviceSpec.from_string("") 56 57_MAXINT32 = 2**31 - 1 58 59DEVICE_PLACEMENT_EXPLICIT = pywrap_tfe.TFE_DEVICE_PLACEMENT_EXPLICIT 60DEVICE_PLACEMENT_WARN = pywrap_tfe.TFE_DEVICE_PLACEMENT_WARN 61DEVICE_PLACEMENT_SILENT = pywrap_tfe.TFE_DEVICE_PLACEMENT_SILENT 62DEVICE_PLACEMENT_SILENT_FOR_INT32 = ( 63 pywrap_tfe.TFE_DEVICE_PLACEMENT_SILENT_FOR_INT32) 64 65SYNC = 0 66ASYNC = 1 67 68MIRRORING_NONE = pywrap_tfe.TFE_MIRRORING_NONE 69MIRRORING_ALL = pywrap_tfe.TFE_MIRRORING_ALL 70 71_KEEP_ALIVE_SECS = 600 72 73_python_eager_context_create_counter = monitoring.Counter( 74 "/tensorflow/api/python/eager_context_create_counter", 75 "Counter for number of eager contexts created in Python.") 76 77 78class _EagerTensorCache(object): 79 """Simple cache which evicts items based on length in a FIFO manner.""" 80 81 def __init__(self, max_items=256, max_tensor_size=10000): 82 self._data = collections.OrderedDict() 83 self._max_items = max_items 84 self._max_tensor_size = max_tensor_size 85 86 def put(self, key, value): 87 if value._num_elements() > self._max_tensor_size: # pylint: disable=protected-access 88 return 89 90 self._data[key] = value 91 92 if len(self._data) > self._max_items: 93 self._data.popitem(last=False) 94 95 def get(self, key): 96 return self._data.get(key, None) 97 98 def flush(self): 99 self._data = {} 100 101 102class FunctionCallOptions(object): 103 """Options applied at call sites of eager functions. 104 105 Eager functions are functions decorated with tf.contrib.eager.defun. 106 """ 107 108 def __init__(self, executor_type=None, config_proto=None): 109 """Constructor. 110 111 Args: 112 executor_type: (optional) name of the executor to be used to execute the 113 eager function. If None or an empty string, the default Tensorflow 114 executor will be used. 115 config_proto: (optional) a `config_pb2.ConfigProto` proto or 116 a serialized string of that proto. 117 The config used by Grappler when optimizing the function graph. 118 Each concrete function is optimized the first time is called. Changing 119 config_proto after the first call has no effect. 120 If config_proto is None, an empty RewriterConfig will be used. 121 """ 122 self.config_proto_serialized = config_proto 123 self.executor_type = executor_type 124 125 @property 126 def executor_type(self): 127 return self._executor_type 128 129 @executor_type.setter 130 def executor_type(self, executor_type): 131 self._executor_type = executor_type 132 133 @property 134 def config_proto_serialized(self): 135 return self._config_proto_serialized 136 137 @config_proto_serialized.setter 138 def config_proto_serialized(self, config): 139 if isinstance(config, config_pb2.ConfigProto): 140 self._config_proto_serialized = config.SerializeToString() 141 elif isinstance(config, str): 142 self._config_proto_serialized = config 143 elif config is None: 144 self._config_proto_serialized = ( 145 config_pb2.ConfigProto().SerializeToString()) 146 else: 147 raise ValueError("the rewriter config must be either a " 148 "config_pb2.ConfigProto, or a serialized string of that " 149 "proto or None. got: {}".format(type(config))) 150 151 152# Map from context_id (an int) to _TensorCaches. 153# Dicts are thread safe in CPython. 154# TODO(iga): Remove this once TensorCaches are moved to C++. 155_tensor_caches_map = {} 156 157 158class _TensorCaches(threading.local): 159 """Thread local tensor caches.""" 160 161 def __init__(self): 162 super(_TensorCaches, self).__init__() 163 self._ones_rank_cache = None 164 self._zeros_cache = None 165 166 @property 167 def ones_rank_cache(self): 168 if not self._ones_rank_cache: 169 self._ones_rank_cache = _EagerTensorCache() 170 return self._ones_rank_cache 171 172 @property 173 def zeros_cache(self): 174 if not self._zeros_cache: 175 self._zeros_cache = _EagerTensorCache() 176 return self._zeros_cache 177 178 179class _ThreadLocalData(threading.local): 180 """Thread local storage for the eager context.""" 181 182 def __init__(self): 183 super(_ThreadLocalData, self).__init__() 184 self.device_spec = _starting_device_spec 185 self.device_name = "" 186 self.is_eager = default_execution_mode == EAGER_MODE 187 self.scope_name = "" 188 self.function_call_options = None 189 self.executor = None 190 self.op_callbacks = [] 191 self.invoking_op_callbacks = False 192 193 194ContextSwitch = collections.namedtuple( 195 "ContextSwitch", ["is_building_function", "enter_context_fn", 196 "device_stack"]) 197 198 199# `_ContextSwitchStack` is a `threading.local` to match the semantics of 200# ``DefaultGraphStack`, which is also a `threading.local`. 201class _ContextSwitchStack(threading.local): 202 """A thread-local stack of context switches.""" 203 204 def __init__(self, eager): 205 super(_ContextSwitchStack, self).__init__() 206 self.stack = [] 207 if eager: 208 # Initialize the stack with a pointer to enter the eager context; this 209 # ensures that the fact that eager execution was enabled is propagated 210 # across threads, since (1) `enable_eager_execution` modifies a 211 # process-level flag (`default_execution_mode`) and (2) `__init__` is 212 # called each time a threading.local object is used in a separate thread. 213 self.push(is_building_function=False, enter_context_fn=eager_mode, 214 device_stack=None) 215 216 def push(self, is_building_function, enter_context_fn, device_stack): 217 """Push metadata about a context switch onto the stack. 218 219 A context switch can take any one of the two forms: installing a graph as 220 the default graph, or entering the eager context. For each context switch, 221 we record whether or not the entered context is building a function. 222 223 Args: 224 is_building_function: (bool.) Whether the context is building a function. 225 enter_context_fn: (function.) A callable that executes the context switch. 226 For example, `graph.as_default` or `eager_mode`. 227 device_stack: If applicable, the device function stack for this 228 graph. When breaking out of graphs in init_scope, the innermost nonempty 229 device stack is used. Eager contexts put `None` here and the value is 230 never used. 231 """ 232 233 self.stack.append( 234 ContextSwitch(is_building_function, enter_context_fn, device_stack)) 235 236 def pop(self): 237 """Pop the stack.""" 238 239 self.stack.pop() 240 241 242@tf_export("config.LogicalDevice") 243class LogicalDevice( 244 collections.namedtuple("LogicalDevice", ["name", "device_type"])): 245 """Abstraction for a logical device initialized by the runtime. 246 247 A `tf.config.LogicalDevice` corresponds to an initialized logical device on a 248 `tf.config.PhysicalDevice` or a remote device visible to the cluster. Tensors 249 and operations can be placed on a specific logical device by calling 250 `tf.device` with a specified `tf.config.LogicalDevice`. 251 252 Fields: 253 name: The fully qualified name of the device. Can be used for Op or function 254 placement. 255 device_type: String declaring the type of device such as "CPU" or "GPU". 256 """ 257 pass 258 259 260@tf_export("config.LogicalDeviceConfiguration", 261 "config.experimental.VirtualDeviceConfiguration") 262class LogicalDeviceConfiguration( 263 collections.namedtuple("LogicalDeviceConfiguration", ["memory_limit"])): 264 """Configuration class for a logical devices. 265 266 The class specifies the parameters to configure a `tf.config.PhysicalDevice` 267 as it is initialized to a `tf.config.LogicalDevice` during runtime 268 initialization. Not all fields are valid for all device types. 269 270 See `tf.config.get_logical_device_configuration` and 271 `tf.config.set_logical_device_configuration` for usage examples. 272 273 Fields: 274 memory_limit: (optional) Maximum memory (in MB) to allocate on the virtual 275 device. Currently only supported for GPUs. 276 """ 277 278 def __new__(cls, memory_limit=None): 279 return super(LogicalDeviceConfiguration, cls).__new__(cls, memory_limit) 280 281 282@tf_export("config.PhysicalDevice") 283class PhysicalDevice( 284 collections.namedtuple("PhysicalDevice", ["name", "device_type"])): 285 """Abstraction for a locally visible physical device. 286 287 TensorFlow can utilize various devices such as the CPU or multiple GPUs 288 for computation. Before initializing a local device for use, the user can 289 customize certain properties of the device such as it's visibility or memory 290 configuration. 291 292 Once a visible `tf.config.PhysicalDevice` is initialized one or more 293 `tf.config.LogicalDevice` objects are created. Use 294 `tf.config.set_visible_devices` to configure the visibility of a physical 295 device and `tf.config.set_logical_device_configuration` to configure multiple 296 `tf.config.LogicalDevice` objects for a `tf.config.PhysicalDevice`. This is 297 useful when separation between models is needed or to simulate a multi-device 298 environment. 299 300 Fields: 301 name: Unique identifier for device. 302 device_type: String declaring the type of device such as "CPU" or "GPU". 303 """ 304 pass 305 306 307class _AtomicCounter(object): 308 """A simple atomic counter.""" 309 310 def __init__(self): 311 self._value = 0 312 self._lock = threading.Lock() 313 314 def increment_and_get(self): 315 with self._lock: 316 self._value += 1 317 return self._value 318 319 320_context_id_counter = _AtomicCounter() 321 322 323class _TensorCacheDeleter(object): 324 """Deletes tensor caches for a given context.""" 325 326 def __init__(self, context_id): 327 self._context_id = context_id 328 329 def __del__(self): 330 if _tensor_caches_map is None: 331 return 332 if self._context_id in _tensor_caches_map: 333 del _tensor_caches_map[self._context_id] 334 335 336# TODO(agarwal): rename to EagerContext / EagerRuntime ? 337# TODO(agarwal): consider keeping the corresponding Graph here. 338class Context(object): 339 """Environment in which eager operations execute.""" 340 341 # TODO(agarwal): create and link in some documentation for `execution_mode`. 342 # pylint: disable=redefined-outer-name 343 def __init__(self, 344 config=None, 345 device_policy=None, 346 execution_mode=None, 347 server_def=None): 348 """Creates a new Context. 349 350 Args: 351 config: (Optional.) A `ConfigProto` protocol buffer with configuration 352 options for the Context. Note that a lot of these options may be 353 currently unimplemented or irrelevant when eager execution is enabled. 354 device_policy: (Optional.) What policy to use when trying to run an 355 operation on a device with inputs which are not on that device. 356 When set to None, an appropriate value will be picked automatically. 357 The value picked may change between TensorFlow releases. 358 359 Defaults to DEVICE_PLACEMENT_SILENT. 360 Valid values: 361 - DEVICE_PLACEMENT_EXPLICIT: raises an error if the placement is 362 not correct. 363 - DEVICE_PLACEMENT_WARN: copies the tensors which are not on the 364 right device but raises a warning. 365 - DEVICE_PLACEMENT_SILENT: silently copies the tensors. This might 366 hide performance problems. 367 - DEVICE_PLACEMENT_SILENT_FOR_INT32: silently copies int32 tensors, 368 raising errors on the other ones. 369 execution_mode: (Optional.) Policy controlling how operations dispatched 370 are actually executed. When set to None, an appropriate value will be 371 picked automatically. The value picked may change between TensorFlow 372 releases. 373 Valid values: 374 - SYNC: executes each operation synchronously. 375 - ASYNC: executes each operation asynchronously. These 376 operations may return "non-ready" handles. 377 server_def: (Optional.) A tensorflow::ServerDef proto. 378 Enables execution on remote devices. GrpcServers need to be started by 379 creating an identical server_def to this, and setting the appropriate 380 task_indexes, so that the servers can communicate. It will then be 381 possible to execute operations on remote devices. 382 383 Raises: 384 ValueError: If execution_mode is not valid. 385 """ 386 # This _id is used only to index the tensor caches. 387 # TODO(iga): Remove this when tensor caches are moved to C++. 388 self._id = _context_id_counter.increment_and_get() 389 self._tensor_cache_deleter = _TensorCacheDeleter(self._id) 390 _tensor_caches_map[self._id] = _TensorCaches() 391 392 self._config = config 393 self._thread_local_data = _ThreadLocalData() 394 self._context_switches = _ContextSwitchStack(self.executing_eagerly()) 395 self._context_handle = None 396 self._context_devices = None 397 self._seed = None 398 self._initialize_lock = threading.Lock() 399 self._initialized = False 400 if device_policy is None: 401 device_policy = DEVICE_PLACEMENT_SILENT 402 self._device_policy = device_policy 403 self._mirroring_policy = None 404 if execution_mode not in (None, SYNC, ASYNC): 405 raise ValueError( 406 "execution_mode should be None/SYNC/ASYNC. Got %s" % execution_mode) 407 if execution_mode is None: 408 execution_mode = SYNC 409 self._default_is_async = execution_mode == ASYNC 410 self._lazy_remote_inputs_copy = None 411 self._server_def = server_def 412 self._collective_ops_server_def = None 413 self._collective_leader = None 414 self._collective_scoped_allocator_enabled_ops = None 415 self._collective_use_nccl_communication = None 416 self._collective_device_filters = None 417 418 self._device_lock = threading.Lock() 419 self._physical_devices = None 420 self._visible_device_list = [] 421 self._memory_growth_map = None 422 self._virtual_device_map = {} 423 424 # Values set after construction 425 self._optimizer_jit = None 426 self._intra_op_parallelism_threads = None 427 self._inter_op_parallelism_threads = None 428 self._soft_device_placement = None 429 self._log_device_placement = None 430 self._enable_mlir_bridge = None 431 self._optimizer_experimental_options = {} 432 433 _python_eager_context_create_counter.get_cell().increase_by(1) 434 # pylint: enable=redefined-outer-name 435 436 def _set_global_seed(self, seed): 437 """Set a global eager mode seed for random ops.""" 438 self._seed = seed 439 # `random.Random(seed)` needs `seed` to be hashable, while values of type 440 # e.g. `np.int64` or `np.ndarray` are not. We use `int(...)` to convert them 441 # to int. 442 try: 443 hash(seed) 444 except TypeError: 445 seed = int(np.array(seed)) 446 self._rng = random.Random(seed) 447 # Also clear the kernel cache, to reset any existing seeds 448 if self._context_handle is not None: 449 pywrap_tfe.TFE_ContextClearCaches(self._context_handle) 450 451 def _internal_operation_seed(self): 452 """Returns a fake operation seed. 453 454 In eager mode, user shouldn't set or depend on operation seed. 455 Here, we generate a random seed based on global seed to make 456 operation's randomness different and depend on the global seed. 457 458 Returns: 459 A fake operation seed based on global seed. 460 """ 461 return self._rng.randint(0, _MAXINT32) 462 463 def _initialize_logical_devices(self): 464 """Helper to initialize devices.""" 465 # Store list of devices 466 logical_devices = [] 467 context_devices = [] 468 device_list = pywrap_tfe.TFE_ContextListDevices(self._context_handle) 469 try: 470 self._num_gpus = 0 471 for i in range(pywrap_tfe.TF_DeviceListCount(device_list)): 472 dev_name = pywrap_tfe.TF_DeviceListName(device_list, i) 473 context_devices.append(pydev.canonical_name(dev_name)) 474 spec = pydev.DeviceSpec.from_string(dev_name) 475 # If the job is localhost, we assume that the cluster has not yet been 476 # configured and thus clear the job, replica & task. 477 if spec.job == "localhost": 478 spec = spec.replace(job=None, replica=None, task=None) 479 logical_devices.append( 480 LogicalDevice(name=spec.to_string(), device_type=spec.device_type)) 481 dev_type = pywrap_tfe.TF_DeviceListType(device_list, i) 482 if dev_type == "GPU": 483 self._num_gpus += 1 484 485 finally: 486 self._logical_devices = logical_devices 487 self._context_devices = context_devices 488 pywrap_tfe.TF_DeleteDeviceList(device_list) 489 490 def ensure_initialized(self): 491 """Initialize handle and devices if not already done so.""" 492 if self._initialized: 493 return 494 with self._initialize_lock: 495 if self._initialized: 496 return 497 assert self._context_devices is None 498 opts = pywrap_tfe.TFE_NewContextOptions() 499 try: 500 config_str = self.config.SerializeToString() 501 pywrap_tfe.TFE_ContextOptionsSetConfig(opts, config_str) 502 if self._device_policy is not None: 503 pywrap_tfe.TFE_ContextOptionsSetDevicePlacementPolicy( 504 opts, self._device_policy) 505 if self._mirroring_policy is not None: 506 pywrap_tfe.TFE_ContextOptionsSetMirroringPolicy( 507 opts, self._mirroring_policy) 508 if self._default_is_async == ASYNC: 509 pywrap_tfe.TFE_ContextOptionsSetAsync(opts, True) 510 if self._lazy_remote_inputs_copy is not None: 511 pywrap_tfe.TFE_ContextOptionsSetLazyRemoteInputsCopy( 512 opts, self._lazy_remote_inputs_copy) 513 context_handle = pywrap_tfe.TFE_NewContext(opts) 514 finally: 515 pywrap_tfe.TFE_DeleteContextOptions(opts) 516 assert not (self._server_def and self._collective_ops_server_def), ( 517 "Cannot enable remote execution as well as collective ops at the " 518 "moment. If this is important to you, please file an issue.") 519 if self._server_def is not None: 520 server_def_str = self._server_def.SerializeToString() 521 pywrap_tfe.TFE_ContextSetServerDef(context_handle, _KEEP_ALIVE_SECS, 522 server_def_str) 523 elif self._collective_ops_server_def is not None: 524 server_def_str = self._collective_ops_server_def.SerializeToString() 525 pywrap_tfe.TFE_EnableCollectiveOps(context_handle, server_def_str) 526 527 self._context_handle = context_handle 528 self._initialize_logical_devices() 529 self._initialized = True 530 531 def _clear_caches(self): 532 self.ones_rank_cache().flush() 533 self.zeros_cache().flush() 534 pywrap_tfe.TFE_ClearScalarCache() 535 536 def get_server_def(self): 537 return self._server_def 538 539 def set_server_def(self, server_def, keep_alive_secs=_KEEP_ALIVE_SECS): 540 """Allow setting a server_def on the context. 541 542 When a server def is replaced, it effectively clears a bunch of caches 543 within the context. If you attempt to use a tensor object that was pointing 544 to a tensor on the remote device, it will raise an error. 545 546 Args: 547 server_def: A tensorflow::ServerDef proto. 548 Enables execution on remote devices. 549 keep_alive_secs: Num. seconds after which the remote end will hang up. 550 As long as the client is still alive, the server state for the context 551 will be kept alive. If the client is killed (or there is some failure), 552 the server will clean up its context keep_alive_secs after the final RPC 553 it receives. 554 555 Raises: 556 ValueError: if server_def is None. 557 """ 558 if not server_def: 559 raise ValueError("server_def is None.") 560 561 self._server_def = server_def 562 563 if self._context_handle: 564 server_def_str = server_def.SerializeToString() 565 pywrap_tfe.TFE_ContextSetServerDef(self._context_handle, keep_alive_secs, 566 server_def_str) 567 self._initialize_logical_devices() 568 569 # Clear all the caches in case there are remote tensors in them. 570 self._clear_caches() 571 572 def update_server_def(self, server_def, keep_alive_secs=_KEEP_ALIVE_SECS): 573 """Update a server_def on the context. 574 575 Args: 576 server_def: A tensorflow::ServerDef proto. Enables execution on remote 577 devices. 578 keep_alive_secs: Num. seconds after which the remote end will hang up. As 579 long as the client is still alive, the server state for the context will 580 be kept alive. If the client is killed (or there is some failure), the 581 server will clean up its context keep_alive_secs after the final RPC it 582 receives. 583 584 Raises: 585 ValueError: if server_def is None. 586 """ 587 if not server_def: 588 raise ValueError("server_def is None.") 589 590 self._server_def = server_def 591 592 if self._context_handle: 593 server_def_str = server_def.SerializeToString() 594 # Current executor might have pending nodes that involves updated remote 595 # devices. Wait for them to finish before updating. 596 self.executor.wait() 597 self.executor.clear_error() 598 pywrap_tfe.TFE_ContextUpdateServerDef(self._context_handle, 599 keep_alive_secs, server_def_str) 600 self._initialize_logical_devices() 601 602 self._clear_caches() 603 604 def check_alive(self, worker_name): 605 """Checks whether a remote worker is alive or not. 606 607 Args: 608 worker_name: a string representing the remote worker. It must be a fully 609 specified name like "/job:worker/replica:0/task:0". 610 611 Returns: 612 a boolean indicating whether the remote worker is alive or not. 613 614 Raises: 615 ValueError: if context is not initialized. 616 """ 617 # TODO(yuefengz): support checking multiple workers. 618 if self._context_handle: 619 return pywrap_tfe.TFE_ContextCheckAlive(self._context_handle, worker_name) 620 else: 621 raise ValueError("Context is not initialized.") 622 623 def enable_collective_ops(self, server_def): 624 """Enable distributed collective ops with an appropriate server_def. 625 626 Args: 627 server_def: A tensorflow::ServerDef proto. Enables execution on remote 628 devices. 629 630 Raises: 631 ValueError: if server_def is None. 632 RuntimeError: if this method is not called at program startup. 633 """ 634 if not server_def: 635 raise ValueError("server_def is None.") 636 637 # TODO(b/129298253): Allow creating datasets/tensors before enabling 638 # collective ops. 639 if self._context_handle is not None: 640 logging.warning("Enabling collective ops after program startup may cause " 641 "error when accessing previously created tensors.") 642 643 self._collective_ops_server_def = server_def 644 645 def configure_collective_ops( 646 self, 647 collective_leader="", 648 scoped_allocator_enabled_ops=("CollectiveReduce",), 649 use_nccl_communication=False, 650 device_filters=None): 651 """Configure collective ops. 652 653 Collective group leader is necessary for collective ops to run, other 654 configurations are mainly for the purpose of performance. 655 656 Args: 657 collective_leader: a device string for collective leader, e.g. 658 "/job:worker/replica:0/task:0"; empty string means local execution of 659 collective ops. 660 scoped_allocator_enabled_ops: a tuple or a list of op names for scoped 661 allocator to run with. 662 use_nccl_communication: whether to use nccl communication for collective 663 ops. 664 device_filters: a tuple or a list of device strings. If set, corresponding 665 task can only see the devices filtered by these device filters. 666 667 Raises: 668 RuntimeError: if this method is not called at program startup. 669 """ 670 if self._collective_leader is not None: 671 if (self._collective_leader != collective_leader or 672 self._collective_scoped_allocator_enabled_ops != 673 scoped_allocator_enabled_ops or 674 self._collective_use_nccl_communication != use_nccl_communication or 675 self._collective_device_filters != device_filters): 676 raise ValueError("Collective ops are already configured.") 677 else: 678 return 679 680 if self._context_handle is not None: 681 raise RuntimeError("Collective ops must be configured at program startup") 682 683 self._collective_leader = collective_leader 684 self._collective_scoped_allocator_enabled_ops = scoped_allocator_enabled_ops 685 self._collective_use_nccl_communication = use_nccl_communication 686 self._collective_device_filters = device_filters 687 688 @property 689 def _handle(self): 690 if self._context_handle is None: 691 raise AssertionError("Context must be initialized first.") 692 693 return self._context_handle 694 695 @property 696 def _devices(self): 697 if self._context_devices is None: 698 raise AssertionError("Context must be initialized first.") 699 700 return self._context_devices 701 702 def __str__(self): 703 if self._context_handle is None: 704 return "Eager TensorFlow Context. Devices currently uninitialized." 705 else: 706 devices = self._devices 707 lines = ["Eager TensorFlow Context with %d devices" % (len(devices))] 708 for i, d in enumerate(devices): 709 lines.append(" Device %d: %s" % (i, d)) 710 return "\n".join(lines) 711 712 @tf_contextlib.contextmanager 713 def _mode(self, mode): 714 """A context manager to allow setting the mode to EAGER/GRAPH.""" 715 ctx = self._thread_local_data 716 old_is_eager = ctx.is_eager 717 ctx.is_eager = mode == EAGER_MODE 718 if mode == EAGER_MODE: 719 # Entering graph mode does not provide us with sufficient information to 720 # record a context switch; graph-based context switches are only logged 721 # when a graph is registered as the default graph. 722 self.context_switches.push(False, eager_mode, None) 723 try: 724 yield 725 finally: 726 ctx.is_eager = old_is_eager 727 if mode == EAGER_MODE: 728 self.context_switches.pop() 729 730 def executing_eagerly(self): 731 """Returns True if current thread has eager executing enabled.""" 732 return self._thread_local_data.is_eager 733 734 def ones_rank_cache(self): 735 """Per-device cache for scalars.""" 736 return _tensor_caches_map[self._id].ones_rank_cache 737 738 def zeros_cache(self): 739 """Per-device cache for scalars.""" 740 return _tensor_caches_map[self._id].zeros_cache 741 742 @property 743 def scope_name(self): 744 """Returns scope name for the current thread.""" 745 return self._thread_local_data.scope_name 746 747 @scope_name.setter 748 def scope_name(self, s): 749 """Sets scope name for the current thread.""" 750 self._thread_local_data.scope_name = s 751 752 @property 753 def device_name(self): 754 """Returns the device name for the current thread.""" 755 return self._thread_local_data.device_name 756 757 @property 758 def device_spec(self): 759 """Returns the device spec for the current thread.""" 760 return self._thread_local_data.device_spec 761 762 def _set_device(self, device_name, device_spec): 763 self._thread_local_data.device_name = device_name 764 self._thread_local_data.device_spec = device_spec 765 766 def device(self, name): 767 """Context-manager to force placement of operations and Tensors on a device. 768 769 Args: 770 name: Name of the device or None to get default placement. 771 772 Returns: 773 Context manager that forces device placement. 774 775 Raises: 776 ValueError: If name is not a string or is an invalid device name. 777 RuntimeError: If device scopes are not properly nested. 778 """ 779 if isinstance(name, LogicalDevice): 780 name = name.name 781 elif pydev.is_device_spec(name): 782 name = name.to_string() 783 return _EagerDeviceContext(self, name) 784 785 def devices(self): 786 """List of the names of devices available to execute operations.""" 787 return self._devices 788 789 def host_address_space(self): 790 self.ensure_initialized() 791 with c_api_util.tf_buffer() as buffer_: 792 pywrap_tfe.TFE_HostAddressSpace(self._context_handle, buffer_) 793 address_space = pywrap_tf_session.TF_GetBuffer(buffer_).decode("utf-8") 794 return address_space 795 796 # TODO(fishx): remove this property. 797 @property 798 def execution_mode(self): 799 """Gets execution mode for current thread.""" 800 return ASYNC if self.is_async() else SYNC 801 802 @execution_mode.setter 803 def execution_mode(self, mode): 804 """Sets execution mode for current thread.""" 805 if mode not in (None, SYNC, ASYNC): 806 raise ValueError( 807 "Execution mode should be None/SYNC/ASYNC. Got %s" % mode) 808 809 if mode is None: 810 mode = SYNC 811 812 enable_async = (mode == ASYNC) 813 if self.is_async() != enable_async: 814 # Only set the execution mode if the context has already been initialized 815 if self._context_handle is not None: 816 self.executor.wait() 817 executor_new = executor.new_executor(enable_async) 818 self._thread_local_data.executor = executor_new 819 pywrap_tfe.TFE_ContextSetExecutorForThread(self._context_handle, 820 executor_new.handle()) 821 else: 822 self._default_is_async = enable_async 823 824 def is_async(self): 825 if self._context_handle is not None: 826 return self.executor.is_async() 827 else: 828 return self._default_is_async 829 830 @property 831 def executor(self): 832 ensure_initialized() 833 return executor.Executor( 834 pywrap_tfe.TFE_ContextGetExecutorForThread(self._context_handle)) 835 836 @executor.setter 837 def executor(self, e): 838 ensure_initialized() 839 pywrap_tfe.TFE_ContextSetExecutorForThread(self._context_handle, e.handle()) 840 841 @property 842 def config(self): 843 """Return the ConfigProto with all runtime deltas applied.""" 844 # Ensure physical devices have been discovered and config has been imported 845 self._initialize_physical_devices() 846 847 config = config_pb2.ConfigProto() 848 if self._config is not None: 849 config.CopyFrom(self._config) 850 851 if self._optimizer_jit is not None: 852 config.graph_options.optimizer_options.global_jit_level = ( 853 config_pb2.OptimizerOptions.ON_1 854 if self._optimizer_jit else config_pb2.OptimizerOptions.OFF) 855 if self._intra_op_parallelism_threads is not None: 856 config.intra_op_parallelism_threads = self._intra_op_parallelism_threads 857 if self._inter_op_parallelism_threads is not None: 858 config.inter_op_parallelism_threads = self._inter_op_parallelism_threads 859 860 if self._soft_device_placement is not None: 861 config.allow_soft_placement = self._soft_device_placement 862 else: 863 config.allow_soft_placement = self.executing_eagerly() 864 865 if self._log_device_placement is not None: 866 config.log_device_placement = self._log_device_placement 867 868 if self._enable_mlir_bridge is not None: 869 config.experimental.enable_mlir_bridge = self._enable_mlir_bridge 870 871 def rewriter_toggle(option): 872 toggle = self._optimizer_experimental_options.get(option, None) 873 if toggle is None: 874 return 875 876 setattr(config.graph_options.rewrite_options, 877 option, 878 (rewriter_config_pb2.RewriterConfig.ON 879 if toggle else rewriter_config_pb2.RewriterConfig.OFF)) 880 881 def rewriter_bool(option): 882 toggle = self._optimizer_experimental_options.get(option, None) 883 if toggle is None: 884 return 885 886 setattr(config.graph_options.rewrite_options, 887 option, 888 toggle) 889 890 rewriter_toggle("layout_optimizer") 891 rewriter_toggle("constant_folding") 892 rewriter_toggle("shape_optimization") 893 rewriter_toggle("remapping") 894 rewriter_toggle("arithmetic_optimization") 895 rewriter_toggle("dependency_optimization") 896 rewriter_toggle("loop_optimization") 897 rewriter_toggle("function_optimization") 898 rewriter_toggle("debug_stripper") 899 rewriter_bool("disable_model_pruning") 900 rewriter_toggle("scoped_allocator_optimization") 901 rewriter_toggle("pin_to_host_optimization") 902 rewriter_toggle("implementation_selector") 903 rewriter_toggle("auto_mixed_precision") 904 rewriter_bool("disable_meta_optimizer") 905 nodes = self._optimizer_experimental_options.get("min_graph_nodes", None) 906 if nodes is not None: 907 config.graph_options.rewrite_options.min_graph_nodes = nodes 908 909 # Compute device counts 910 config.device_count["CPU"] = 0 911 config.device_count["GPU"] = 0 912 for dev in self._physical_devices: 913 if dev not in self._visible_device_list: 914 continue 915 916 virtual_devices = self._virtual_device_map.get(dev) 917 if virtual_devices is None: 918 config.device_count[dev.device_type] += 1 919 else: 920 config.device_count[dev.device_type] += len(virtual_devices) 921 922 # Configure gpu_options 923 gpu_options = self._compute_gpu_options() 924 config.gpu_options.MergeFrom(gpu_options) 925 926 # Configure collective ops 927 if self._collective_leader: 928 config.experimental.collective_group_leader = self._collective_leader 929 if self._collective_scoped_allocator_enabled_ops: 930 rewrite_options = config.graph_options.rewrite_options 931 rewrite_options.scoped_allocator_optimization = ( 932 rewriter_config_pb2.RewriterConfig.ON) 933 del rewrite_options.scoped_allocator_opts.enable_op[:] 934 for op in self._collective_scoped_allocator_enabled_ops: 935 rewrite_options.scoped_allocator_opts.enable_op.append(op) 936 if self._collective_use_nccl_communication: 937 config.experimental.collective_nccl = True 938 if self._collective_device_filters: 939 del config.device_filters[:] 940 for f in self._collective_device_filters: 941 config.device_filters.append(f) 942 943 return config 944 945 def _compute_gpu_options(self): 946 """Build the GPUOptions proto.""" 947 visible_device_list = [] 948 virtual_devices = [] 949 gpu_index = -1 950 memory_growths = set() 951 for dev in self.list_physical_devices("GPU"): 952 gpu_index += 1 953 954 if dev not in self._visible_device_list: 955 continue 956 957 growth = self._memory_growth_map[dev] 958 memory_growths.add(growth) 959 visible_device_list.append(str(gpu_index)) 960 961 if self._virtual_device_map: 962 vdevs = self._virtual_device_map.get(dev, []) 963 device_limits = [] 964 for virt_dev in vdevs: 965 device_limits.append(virt_dev.memory_limit) 966 967 virtual_devices.append( 968 config_pb2.GPUOptions.Experimental.VirtualDevices( 969 memory_limit_mb=device_limits)) 970 971 # Only compute growth if virtual devices have not been configured and we 972 # have GPUs 973 if not virtual_devices and memory_growths: 974 if len(memory_growths) > 1: 975 raise ValueError("Memory growth cannot differ between GPU devices") 976 allow_growth = memory_growths.pop() 977 else: 978 allow_growth = None 979 980 return config_pb2.GPUOptions( 981 allow_growth=allow_growth, 982 visible_device_list=",".join(visible_device_list), 983 experimental=config_pb2.GPUOptions.Experimental( 984 virtual_devices=virtual_devices)) 985 986 @property 987 def function_call_options(self): 988 """Returns function call options for current thread. 989 990 Note that the returned object is still referenced by the eager context. 991 992 Returns: the FunctionCallOptions for current thread. 993 """ 994 if self._thread_local_data.function_call_options is None: 995 config = self.config 996 997 # Default to soft placement for functions unless specified 998 if self._soft_device_placement is None: 999 config.allow_soft_placement = True 1000 self._thread_local_data.function_call_options = FunctionCallOptions( 1001 config_proto=config) 1002 1003 return self._thread_local_data.function_call_options 1004 1005 @function_call_options.setter 1006 def function_call_options(self, options): 1007 """Returns function call options for current thread.""" 1008 self._thread_local_data.function_call_options = options 1009 1010 def num_gpus(self): 1011 """The number of GPUs available to execute operations.""" 1012 self.ensure_initialized() 1013 return self._num_gpus 1014 1015 def add_function(self, fn): 1016 """Add a function definition to the context. 1017 1018 Once added, the function (identified by its name) can be executed like any 1019 other operation. 1020 1021 Args: 1022 fn: A wrapped TF_Function (returned from TF_GraphToFunction_wrapper). 1023 """ 1024 self.ensure_initialized() 1025 pywrap_tfe.TFE_ContextAddFunction(self._handle, fn) 1026 1027 def add_function_def(self, fdef): 1028 """Add a function definition to the context. 1029 1030 Once added, the function (identified by its name) can be executed like any 1031 other operation. 1032 1033 Args: 1034 fdef: A FunctionDef protocol buffer message. 1035 """ 1036 self.ensure_initialized() 1037 fdef_string = fdef.SerializeToString() 1038 pywrap_tfe.TFE_ContextAddFunctionDef(self._handle, fdef_string, 1039 len(fdef_string)) 1040 1041 def remove_function(self, name): 1042 """Remove a function from the context. 1043 1044 Once removed, the function cannot be executed anymore. 1045 1046 Args: 1047 name: function signature name. 1048 """ 1049 self.ensure_initialized() 1050 pywrap_tfe.TFE_ContextRemoveFunction(self._handle, name) 1051 1052 def has_function(self, name): 1053 """Check if a function `name` is registered.""" 1054 self.ensure_initialized() 1055 return bool(pywrap_tfe.TFE_ContextHasFunction(self._handle, name)) 1056 1057 def add_op_callback(self, callback): 1058 """Add a post-op callback to the context. 1059 1060 A post-op callback is invoked immediately after an eager operation or 1061 function has finished execution or after a op has been added to a graph, 1062 providing access to the op's type, name input and output tensors. Multiple 1063 op callbacks can be added, in which case the callbacks will be invoked in 1064 the order in which they are added. 1065 1066 Args: 1067 callback: a callable of the signature 1068 `f(op_type, inputs, attrs, outputs, op_name=None, graph=None)`. 1069 See doc strings in `op_callbacks.py` for details on the function 1070 signature and its semantics. 1071 """ 1072 if callback not in self._thread_local_data.op_callbacks: 1073 self._thread_local_data.op_callbacks.append(callback) 1074 1075 def remove_op_callback(self, callback): 1076 """Remove an already-registered op callback. 1077 1078 Args: 1079 callback: The op callback to be removed. 1080 1081 Raises: 1082 KeyError: If `callback` is not already registered. 1083 """ 1084 if callback not in self._thread_local_data.op_callbacks: 1085 raise KeyError( 1086 "The specified op callback has not been registered, " 1087 "and hence cannot be removed.") 1088 del self._thread_local_data.op_callbacks[ 1089 self._thread_local_data.op_callbacks.index(callback)] 1090 1091 @property 1092 def op_callbacks(self): 1093 return self._thread_local_data.op_callbacks 1094 1095 @property 1096 def invoking_op_callbacks(self): 1097 return self._thread_local_data.invoking_op_callbacks 1098 1099 @invoking_op_callbacks.setter 1100 def invoking_op_callbacks(self, value): 1101 self._thread_local_data.invoking_op_callbacks = value 1102 1103 def _initialize_physical_devices(self): 1104 """Get local devices visible to the system.""" 1105 # We lazy initialize self._physical_devices since we do not want to do this 1106 # the constructor since the backend may not be initialized yet. 1107 with self._device_lock: 1108 if self._physical_devices is not None: 1109 return 1110 1111 devs = pywrap_tfe.TF_ListPhysicalDevices() 1112 self._physical_devices = [ 1113 PhysicalDevice(name=d.decode(), 1114 device_type=d.decode().split(":")[1]) for d in devs] 1115 # Construct the visible device list from all physical devices but ignore 1116 # XLA devices 1117 self._visible_device_list = [ 1118 d for d in self._physical_devices 1119 if not d.device_type.startswith("XLA") 1120 ] 1121 self._memory_growth_map = { 1122 d: None for d in self._physical_devices if d.device_type == "GPU" 1123 } 1124 1125 # Import device settings that may have been passed into the constructor 1126 self._import_config() 1127 1128 def list_physical_devices(self, device_type=None): 1129 """List local devices visible to the system. 1130 1131 This API allows a client to query the devices before they have been 1132 initialized by the eager runtime. Additionally a user can filter by device 1133 type, to get only CPUs or GPUs. 1134 1135 Args: 1136 device_type: Optional device type to limit results to 1137 1138 Returns: 1139 List of PhysicalDevice objects. 1140 """ 1141 self._initialize_physical_devices() 1142 1143 if device_type is None: 1144 return list(self._physical_devices) 1145 1146 return [d for d in self._physical_devices if d.device_type == device_type] 1147 1148 def _import_config(self): 1149 """Import config if passed in during construction. 1150 1151 If Context was created with a ConfigProto such as when calling 1152 tf.compat.v1.enable_eager_execution(), then we need to pull out the 1153 various pieces we might be replacing and import then into our internal 1154 class representation. 1155 """ 1156 if self._config is None: 1157 return 1158 1159 num_cpus = self._config.device_count.get("CPU", 1) 1160 if num_cpus != 1: 1161 cpus = [d for d in self._physical_devices if d.device_type == "CPU"] 1162 if num_cpus == 0: 1163 self.set_visible_devices([], "CPU") 1164 elif num_cpus > 1: 1165 self.set_logical_device_configuration( 1166 cpus[0], [LogicalDeviceConfiguration() for _ in range(num_cpus)]) 1167 1168 # Parse GPU options 1169 gpus = [d for d in self._physical_devices if d.device_type == "GPU"] 1170 1171 # If there are no GPUs detected, simply ignore all the GPU options passed in 1172 # rather than doing any validation checks. 1173 if not gpus: 1174 return 1175 1176 gpu_count = self._config.device_count.get("GPU", None) 1177 1178 visible_gpus = [] 1179 # TODO(gjn): Handle importing existing virtual GPU configuration 1180 visible_indices = self._config.gpu_options.visible_device_list 1181 if visible_indices: 1182 for index in visible_indices.split(","): 1183 if int(index) >= len(gpus): 1184 raise ValueError("Invalid visible device index: %s" % index) 1185 visible_gpus.append(gpus[int(index)]) 1186 else: 1187 visible_gpus = gpus 1188 1189 if gpu_count is not None: 1190 visible_gpus = visible_gpus[:gpu_count] 1191 1192 self.set_visible_devices(visible_gpus, "GPU") 1193 1194 def list_logical_devices(self, device_type=None): 1195 """Return logical devices.""" 1196 self.ensure_initialized() 1197 if device_type is None: 1198 return list(self._logical_devices) 1199 1200 return [d for d in self._logical_devices if d.device_type == device_type] 1201 1202 def get_visible_devices(self, device_type=None): 1203 """Get the list of visible devices.""" 1204 self._initialize_physical_devices() 1205 1206 if device_type is None: 1207 return list(self._visible_device_list) 1208 1209 return [ 1210 d for d in self._visible_device_list if d.device_type == device_type 1211 ] 1212 1213 def set_visible_devices(self, devices, device_type=None): 1214 """Set the list of visible devices.""" 1215 self._initialize_physical_devices() 1216 1217 if not isinstance(devices, list): 1218 devices = [devices] 1219 1220 for d in devices: 1221 if d not in self._physical_devices: 1222 raise ValueError("Unrecognized device: %s" % repr(d)) 1223 if device_type is not None and d.device_type != device_type: 1224 raise ValueError("Unrecognized device: %s" % repr(d)) 1225 1226 visible_device_list = [] 1227 if device_type is not None: 1228 visible_device_list = [ 1229 d for d in self._visible_device_list if d.device_type != device_type 1230 ] 1231 1232 visible_device_list += devices 1233 1234 if self._visible_device_list == visible_device_list: 1235 return 1236 1237 if self._context_handle is not None: 1238 raise RuntimeError( 1239 "Visible devices cannot be modified after being initialized") 1240 1241 self._visible_device_list = visible_device_list 1242 1243 def get_memory_growth(self, dev): 1244 """Get if memory growth is enabled for a PhysicalDevice.""" 1245 self._initialize_physical_devices() 1246 1247 if dev not in self._physical_devices: 1248 raise ValueError("Unrecognized device: %s" % repr(dev)) 1249 1250 return self._memory_growth_map[dev] 1251 1252 def set_memory_growth(self, dev, enable): 1253 """Set if memory growth should be enabled for a PhysicalDevice.""" 1254 self._initialize_physical_devices() 1255 1256 if dev not in self._physical_devices: 1257 raise ValueError("Unrecognized device: %s" % repr(dev)) 1258 1259 if dev in self._virtual_device_map: 1260 raise ValueError( 1261 "Cannot set memory growth on device when virtual devices configured") 1262 1263 if dev.device_type != "GPU": 1264 raise ValueError("Cannot set memory growth on non-GPU devices") 1265 1266 if self._memory_growth_map.get(dev) == enable: 1267 return 1268 1269 if self._context_handle is not None: 1270 raise RuntimeError( 1271 "Physical devices cannot be modified after being initialized") 1272 1273 self._memory_growth_map[dev] = enable 1274 1275 def get_logical_device_configuration(self, dev): 1276 """Get the virtual device configuration for a PhysicalDevice.""" 1277 self._initialize_physical_devices() 1278 1279 if dev not in self._physical_devices: 1280 raise ValueError("Unrecognized device: %s" % repr(dev)) 1281 1282 return self._virtual_device_map.get(dev) 1283 1284 def set_logical_device_configuration(self, dev, virtual_devices): 1285 """Set the virtual device configuration for a PhysicalDevice.""" 1286 self._initialize_physical_devices() 1287 1288 if dev not in self._physical_devices: 1289 raise ValueError("Unrecognized device: %s" % repr(dev)) 1290 1291 if dev.device_type == "CPU": 1292 for vdev in virtual_devices: 1293 if vdev.memory_limit is not None: 1294 raise ValueError("Setting memory limit on CPU virtual devices is " 1295 "currently not supported") 1296 elif dev.device_type == "GPU": 1297 for vdev in virtual_devices: 1298 if vdev.memory_limit is None: 1299 raise ValueError( 1300 "Setting memory limit is required for GPU virtual devices") 1301 else: 1302 raise ValueError("Virtual devices are not supported for %s" % 1303 dev.device_type) 1304 1305 if self._virtual_device_map.get(dev) == virtual_devices: 1306 return 1307 1308 if self._context_handle is not None: 1309 raise RuntimeError( 1310 "Virtual devices cannot be modified after being initialized") 1311 1312 self._virtual_device_map[dev] = virtual_devices 1313 1314 @property 1315 def enable_mlir_bridge(self): 1316 return self._enable_mlir_bridge 1317 1318 @enable_mlir_bridge.setter 1319 def enable_mlir_bridge(self, enabled): 1320 self._enable_mlir_bridge = enabled 1321 1322 self._thread_local_data.function_call_options = None 1323 1324 @property 1325 def optimizer_jit(self): 1326 level = self.config.graph_options.optimizer_options.global_jit_level 1327 return (level == config_pb2.OptimizerOptions.ON_1 or 1328 level == config_pb2.OptimizerOptions.ON_2) 1329 1330 @optimizer_jit.setter 1331 def optimizer_jit(self, enabled): 1332 self._optimizer_jit = enabled 1333 1334 self._thread_local_data.function_call_options = None 1335 1336 def get_optimizer_experimental_options(self): 1337 """Get experimental options for the optimizer. 1338 1339 Returns: 1340 Dictionary of current option values 1341 """ 1342 rewrite_options = self.config.graph_options.rewrite_options 1343 options = {} 1344 1345 def rewriter_toggle(option): 1346 attr = getattr(rewrite_options, option) 1347 if attr != 0: 1348 options[option] = (attr == rewriter_config_pb2.RewriterConfig.ON) 1349 1350 def rewriter_bool(option): 1351 options[option] = getattr(rewrite_options, option) 1352 1353 rewriter_toggle("layout_optimizer") 1354 rewriter_toggle("constant_folding") 1355 rewriter_toggle("shape_optimization") 1356 rewriter_toggle("remapping") 1357 rewriter_toggle("arithmetic_optimization") 1358 rewriter_toggle("dependency_optimization") 1359 rewriter_toggle("loop_optimization") 1360 rewriter_toggle("function_optimization") 1361 rewriter_toggle("debug_stripper") 1362 rewriter_bool("disable_model_pruning") 1363 rewriter_toggle("scoped_allocator_optimization") 1364 rewriter_toggle("pin_to_host_optimization") 1365 rewriter_toggle("implementation_selector") 1366 rewriter_toggle("auto_mixed_precision") 1367 rewriter_bool("disable_meta_optimizer") 1368 1369 if rewrite_options.min_graph_nodes != 0: 1370 options["min_graph_nodes"] = rewrite_options.min_graph_nodes 1371 1372 return options 1373 1374 def set_optimizer_experimental_options(self, options): 1375 """Set experimental options for the optimizer. 1376 1377 Args: 1378 options: Dictionary of options to modify 1379 """ 1380 self._optimizer_experimental_options.update(options) 1381 1382 self._thread_local_data.function_call_options = None 1383 1384 @property 1385 def intra_op_parallelism_threads(self): 1386 return self.config.intra_op_parallelism_threads 1387 1388 @intra_op_parallelism_threads.setter 1389 def intra_op_parallelism_threads(self, num_threads): 1390 if self._intra_op_parallelism_threads == num_threads: 1391 return 1392 1393 if self._context_handle is not None: 1394 raise RuntimeError( 1395 "Intra op parallelism cannot be modified after initialization.") 1396 1397 self._intra_op_parallelism_threads = num_threads 1398 1399 @property 1400 def inter_op_parallelism_threads(self): 1401 return self.config.inter_op_parallelism_threads 1402 1403 @inter_op_parallelism_threads.setter 1404 def inter_op_parallelism_threads(self, num_threads): 1405 if self._inter_op_parallelism_threads == num_threads: 1406 return 1407 1408 if self._context_handle is not None: 1409 raise RuntimeError( 1410 "Inter op parallelism cannot be modified after initialization.") 1411 1412 self._inter_op_parallelism_threads = num_threads 1413 1414 @property 1415 def soft_device_placement(self): 1416 return self.config.allow_soft_placement 1417 1418 @soft_device_placement.setter 1419 def soft_device_placement(self, enabled): 1420 self._soft_device_placement = enabled 1421 1422 self._thread_local_data.function_call_options = None 1423 1424 @property 1425 def log_device_placement(self): 1426 return self.config.log_device_placement 1427 1428 @log_device_placement.setter 1429 def log_device_placement(self, enabled): 1430 if self._log_device_placement == enabled: 1431 return 1432 1433 if self._context_handle is not None: 1434 raise RuntimeError( 1435 "Device placement logging must be set at program startup") 1436 1437 self._log_device_placement = enabled 1438 self._thread_local_data.function_call_options = None 1439 1440 @property 1441 def device_policy(self): 1442 # Only get the policy from the context if it has already been initialized 1443 if self._context_handle is not None: 1444 return pywrap_tfe.TFE_ContextGetDevicePlacementPolicy(self._handle) 1445 1446 return self._device_policy 1447 1448 @device_policy.setter 1449 def device_policy(self, policy): 1450 if policy is None: 1451 policy = DEVICE_PLACEMENT_SILENT 1452 1453 if self._device_policy != policy: 1454 self._device_policy = policy 1455 1456 # Only set the policy if the context has already been initialized 1457 if self._context_handle is not None: 1458 pywrap_tfe.TFE_ContextSetThreadLocalDevicePlacementPolicy( 1459 self._handle, self._device_policy) 1460 1461 @property 1462 def mirroring_policy(self): 1463 # Only get the policy from the context if it has already been initialized 1464 if self._context_handle is not None: 1465 return pywrap_tfe.TFE_ContextGetMirroringPolicy(self._handle) 1466 1467 return self._mirroring_policy 1468 1469 @mirroring_policy.setter 1470 def mirroring_policy(self, policy): 1471 if policy is None: 1472 policy = MIRRORING_NONE 1473 1474 if self._mirroring_policy is None or self._mirroring_policy != policy: 1475 self._mirroring_policy = policy 1476 1477 # Only set the policy if the context has already been initialized 1478 if self._context_handle is not None: 1479 pywrap_tfe.TFE_ContextSetThreadLocalMirroringPolicy( 1480 self._handle, self._mirroring_policy) 1481 1482 @property 1483 def lazy_remote_inputs_copy(self): 1484 return self._lazy_remote_inputs_copy 1485 1486 @lazy_remote_inputs_copy.setter 1487 def lazy_remote_inputs_copy(self, lazy_copy): 1488 """Sets whether to copy remote inputs lazily for functions.""" 1489 if not isinstance(lazy_copy, bool): 1490 raise ValueError("Expecting a boolean but got %s" % type(lazy_copy)) 1491 1492 if self._lazy_remote_inputs_copy != lazy_copy: 1493 if self._initialized: 1494 raise ValueError( 1495 "lazy_remote_inputs_copy should be set before being initialized.") 1496 self._lazy_remote_inputs_copy = lazy_copy 1497 1498 def enable_run_metadata(self): 1499 """Enables tracing of op execution via RunMetadata. 1500 1501 To retrieve the accumulated metadata call context.export_run_metadata() 1502 and to stop tracing call context.disable_run_metadata(). 1503 """ 1504 self.ensure_initialized() 1505 pywrap_tfe.TFE_ContextEnableRunMetadata(self._handle) 1506 1507 def disable_run_metadata(self): 1508 """Disables tracing of op execution via RunMetadata.""" 1509 if not self._context_handle: 1510 return 1511 pywrap_tfe.TFE_ContextDisableRunMetadata(self._context_handle) 1512 1513 def enable_graph_collection(self): 1514 """Enables graph collection of executed functions. 1515 1516 To retrieve the accumulated graphs call context.export_run_metadata() 1517 and to stop collecting graphs call context.disable_graph_collection(). 1518 """ 1519 self.ensure_initialized() 1520 pywrap_tfe.TFE_ContextEnableGraphCollection(self._handle) 1521 1522 def disable_graph_collection(self): 1523 """Disables graph collection of executed functions.""" 1524 if not self._context_handle: 1525 return 1526 pywrap_tfe.TFE_ContextDisableGraphCollection(self._context_handle) 1527 1528 def export_run_metadata(self): 1529 """Returns a RunMetadata proto with accumulated information. 1530 1531 The returned protocol buffer contains information since the most recent call 1532 to either enable_run_metadata or export_run_metadata. 1533 1534 Returns: 1535 A RunMetadata protocol buffer. Or None if not enabled. 1536 """ 1537 if not self._context_handle: 1538 return None 1539 with c_api_util.tf_buffer() as buffer_: 1540 pywrap_tfe.TFE_ContextExportRunMetadata(self._context_handle, buffer_) 1541 proto_data = pywrap_tf_session.TF_GetBuffer(buffer_) 1542 run_metadata = config_pb2.RunMetadata() 1543 run_metadata.ParseFromString(compat.as_bytes(proto_data)) 1544 return run_metadata 1545 1546 @property 1547 def context_switches(self): 1548 """Returns a stack of context switches.""" 1549 return self._context_switches 1550 1551 def start_step(self): 1552 pywrap_tfe.TFE_ContextStartStep(self._handle) 1553 1554 def end_step(self): 1555 pywrap_tfe.TFE_ContextEndStep(self._handle) 1556 1557 1558class _EagerDeviceContext(object): 1559 """Context-manager forcing placement of ops and Tensors on a device.""" 1560 1561 def __init__(self, ctx, device_name): 1562 self._device_name = device_name 1563 self._ctx = ctx 1564 self._stack = [] 1565 1566 def __enter__(self): 1567 ctx = self._ctx 1568 old_device_name = ctx.device_name 1569 old_device_spec = ctx.device_spec 1570 new_device_name = self._device_name 1571 cache_key = (old_device_name, new_device_name) 1572 try: 1573 new_device_name, new_device_spec = _device_parsing_cache[cache_key] 1574 except TypeError: 1575 # Error while trying to compute the cache key. 1576 raise ValueError("Expecting a string device name. Got %s(%s)" % 1577 (type(new_device_name), new_device_name)) 1578 except KeyError: 1579 # Handle a cache miss. 1580 if new_device_name is not None: 1581 if not isinstance(new_device_name, six.string_types): 1582 raise ValueError("Expecting a string device name. Got %s(%s)" % 1583 (type(new_device_name), new_device_name)) 1584 device_spec = pydev.DeviceSpec.from_string(new_device_name) 1585 if old_device_name: 1586 new_device_spec = copy.copy(old_device_spec) 1587 else: 1588 ctx.ensure_initialized() 1589 new_device_spec = pydev.DeviceSpec.from_string( 1590 ctx._context_devices[0]) # pylint: disable=protected-access 1591 new_device_spec = new_device_spec.make_merged_spec(device_spec) 1592 else: 1593 new_device_spec = pydev.DeviceSpec.from_string("") 1594 new_device_name = new_device_spec.to_string() 1595 _device_parsing_cache[cache_key] = (new_device_name, new_device_spec) 1596 1597 ctx._set_device(new_device_name, new_device_spec) # pylint: disable=protected-access 1598 self._stack.append((old_device_name, old_device_spec, new_device_spec)) 1599 1600 def __exit__(self, *ex_info): 1601 ctx = self._ctx 1602 old_device_name, old_device_spec, new_device_spec = self._stack[-1] 1603 if ctx.device_spec is not new_device_spec: 1604 raise RuntimeError( 1605 "Exiting device scope without proper scope nesting") 1606 del self._stack[-1] 1607 ctx._set_device(old_device_name, old_device_spec) # pylint: disable=protected-access 1608 1609 1610# Do not set directly. Use _set_context. 1611_context = None 1612_context_lock = threading.Lock() 1613 1614 1615def _set_context_locked(ctx): 1616 global _context 1617 pywrap_tfe.TFE_Py_SetEagerContext(ctx) 1618 _context = ctx 1619 1620 1621def _set_context(ctx): 1622 with _context_lock: 1623 _set_context_locked(ctx) 1624 1625 1626def _create_context(): 1627 with _context_lock: 1628 if _context is None: 1629 ctx = Context() 1630 _set_context_locked(ctx) 1631 1632 1633def _reset_context(): 1634 """Clears and re-initializes the singleton context. 1635 1636 Should only be used for testing. 1637 """ 1638 global _context 1639 with _context_lock: 1640 if _context is not None: 1641 _context._clear_caches() 1642 _context = None 1643 _create_context() 1644 pywrap_tfe.TFE_ClearScalarCache() 1645 1646 1647def context(): 1648 """Returns a singleton context object.""" 1649 if _context is None: 1650 _create_context() 1651 return _context 1652 1653 1654def context_safe(): 1655 """Returns current context (or None if one hasn't been initialized).""" 1656 return _context 1657 1658 1659def ensure_initialized(): 1660 """Initialize the context.""" 1661 context().ensure_initialized() 1662 1663 1664def set_global_seed(seed): 1665 """Sets the eager mode seed.""" 1666 context()._set_global_seed(seed) # pylint: disable=protected-access 1667 1668 1669def global_seed(): 1670 """Returns the eager mode seed.""" 1671 return context()._seed # pylint: disable=protected-access 1672 1673 1674def internal_operation_seed(): 1675 """Returns the operation seed generated based on global seed.""" 1676 return context()._internal_operation_seed() # pylint: disable=protected-access 1677 1678 1679@tf_export("executing_eagerly", v1=[]) 1680def executing_eagerly(): 1681 """Checks whether the current thread has eager execution enabled. 1682 1683 Eager execution is enabled by default and this API returns `True` 1684 in most of cases. However, this API might return `False` in the following use 1685 cases. 1686 1687 * Executing inside `tf.function`, unless under `tf.init_scope` or 1688 `tf.config.experimental_run_functions_eagerly(True)` is previously called. 1689 * Executing inside a transformation function for `tf.dataset`. 1690 * `tf.compat.v1.disable_eager_execution()` is called. 1691 1692 General case: 1693 1694 >>> print(tf.executing_eagerly()) 1695 True 1696 1697 Inside `tf.function`: 1698 1699 >>> @tf.function 1700 ... def fn(): 1701 ... with tf.init_scope(): 1702 ... print(tf.executing_eagerly()) 1703 ... print(tf.executing_eagerly()) 1704 >>> fn() 1705 True 1706 False 1707 1708 Inside `tf.function` after 1709 1710 `tf.config.experimental_run_functions_eagerly(True)` is called: 1711 >>> tf.config.experimental_run_functions_eagerly(True) 1712 >>> @tf.function 1713 ... def fn(): 1714 ... with tf.init_scope(): 1715 ... print(tf.executing_eagerly()) 1716 ... print(tf.executing_eagerly()) 1717 >>> fn() 1718 True 1719 True 1720 >>> tf.config.experimental_run_functions_eagerly(False) 1721 1722 Inside a transformation function for `tf.dataset`: 1723 1724 >>> def data_fn(x): 1725 ... print(tf.executing_eagerly()) 1726 ... return x 1727 >>> dataset = tf.data.Dataset.range(100) 1728 >>> dataset = dataset.map(data_fn) 1729 False 1730 1731 Returns: 1732 `True` if the current thread has eager execution enabled. 1733 """ 1734 ctx = context_safe() 1735 if ctx is None: 1736 return default_execution_mode == EAGER_MODE 1737 1738 return ctx.executing_eagerly() 1739 1740 1741@tf_export(v1=["executing_eagerly"]) 1742def executing_eagerly_v1(): 1743 """Checks whether the current thread has eager execution enabled. 1744 1745 Eager execution is typically enabled via 1746 `tf.compat.v1.enable_eager_execution`, but may also be enabled within the 1747 context of a Python function via tf.contrib.eager.py_func. 1748 1749 When eager execution is enabled, returns `True` in most cases. However, 1750 this API might return `False` in the following use cases. 1751 1752 * Executing inside `tf.function`, unless under `tf.init_scope` or 1753 `tf.config.experimental_run_functions_eagerly(True)` is previously called. 1754 * Executing inside a transformation function for `tf.dataset`. 1755 * `tf.compat.v1.disable_eager_execution()` is called. 1756 1757 >>> tf.compat.v1.enable_eager_execution() 1758 1759 General case: 1760 1761 >>> print(tf.executing_eagerly()) 1762 True 1763 1764 Inside `tf.function`: 1765 1766 >>> @tf.function 1767 ... def fn(): 1768 ... with tf.init_scope(): 1769 ... print(tf.executing_eagerly()) 1770 ... print(tf.executing_eagerly()) 1771 >>> fn() 1772 True 1773 False 1774 1775 Inside `tf.function` 1776 after `tf.config.experimental_run_functions_eagerly(True)` is called: 1777 1778 >>> tf.config.experimental_run_functions_eagerly(True) 1779 >>> @tf.function 1780 ... def fn(): 1781 ... with tf.init_scope(): 1782 ... print(tf.executing_eagerly()) 1783 ... print(tf.executing_eagerly()) 1784 >>> fn() 1785 True 1786 True 1787 >>> tf.config.experimental_run_functions_eagerly(False) 1788 1789 Inside a transformation function for `tf.dataset`: 1790 1791 >>> def data_fn(x): 1792 ... print(tf.executing_eagerly()) 1793 ... return x 1794 >>> dataset = tf.data.Dataset.range(100) 1795 >>> dataset = dataset.map(data_fn) 1796 False 1797 1798 Returns: 1799 `True` if the current thread has eager execution enabled. 1800 """ 1801 return executing_eagerly() 1802 1803 1804def in_eager_mode(): 1805 """Use executing_eagerly() instead. This function will be removed.""" 1806 return executing_eagerly() 1807 1808 1809def shared_name(name=None): 1810 """Returns the anonymous shared name GUID if no shared name is specified. 1811 1812 In eager mode we need to use a unique shared name to avoid spurious sharing 1813 issues. The runtime generates a unique name on our behalf when the reserved 1814 GUID is used as a shared name. 1815 1816 Args: 1817 name: Optional shared name 1818 1819 Returns: 1820 Eager compatible shared name. 1821 """ 1822 if name or not executing_eagerly(): 1823 return name 1824 1825 # Ensure a unique name when eager execution is enabled to avoid spurious 1826 # sharing issues. 1827 return "cd2c89b7-88b7-44c8-ad83-06c2a9158347" 1828 1829 1830def graph_mode(): 1831 """Context-manager to disable eager execution for the current thread.""" 1832 return context()._mode(GRAPH_MODE) # pylint: disable=protected-access 1833 1834 1835def eager_mode(): 1836 """Context-manager to enable eager execution for the current thread.""" 1837 return context()._mode(EAGER_MODE) # pylint: disable=protected-access 1838 1839 1840def scope_name(): 1841 """Name of the current scope.""" 1842 return context().scope_name 1843 1844 1845def device(name): 1846 """Context-manager to force placement of operations and Tensors on a device. 1847 1848 Example: 1849 ```python 1850 with tf.device('gpu:0'): 1851 with tf.device('cpu:0'): 1852 shape = tf.constant([], dtype=tf.int32) 1853 x = tf.random.truncated_normal(shape, tf.float32) 1854 ``` 1855 will ensure that the `shape` Tensor is on CPU but the `truncated_normal` 1856 operation runs on GPU 0. 1857 1858 Args: 1859 name: Name of the device (see context().devices()), or None to 1860 perform automatic placement. 1861 1862 Returns: 1863 Context manager for setting the device. 1864 """ 1865 ensure_initialized() 1866 return context().device(name) 1867 1868 1869@tf_export("debugging.get_log_device_placement") 1870def get_log_device_placement(): 1871 """Get if device placements are logged. 1872 1873 Returns: 1874 If device placements are logged. 1875 """ 1876 return context().log_device_placement 1877 1878 1879@tf_export("debugging.set_log_device_placement") 1880def set_log_device_placement(enabled): 1881 """Set if device placements should be logged. 1882 1883 Args: 1884 enabled: Whether to enabled device placement logging. 1885 """ 1886 context().log_device_placement = enabled 1887 1888 1889@tf_contextlib.contextmanager 1890def device_policy(policy): 1891 """Context manager for setting device placement policy for current thread.""" 1892 ctx = context() 1893 old_policy = ctx.device_policy 1894 try: 1895 ctx.device_policy = policy 1896 yield 1897 finally: 1898 ctx.device_policy = old_policy 1899 1900 1901@tf_contextlib.contextmanager 1902def mirroring_policy(policy): 1903 """Context manager for setting mirroring policy for current thread.""" 1904 ctx = context() 1905 old_policy = ctx.mirroring_policy 1906 try: 1907 ctx.mirroring_policy = policy 1908 yield 1909 finally: 1910 ctx.mirroring_policy = old_policy 1911 1912 1913def set_execution_mode(mode): 1914 """Sets execution mode for the current thread.""" 1915 context().execution_mode = mode 1916 1917 1918# TODO(fishx): remove this method. 1919@tf_contextlib.contextmanager 1920def execution_mode(mode): 1921 """Context manager for setting execution mode for current thread.""" 1922 if mode is None: 1923 yield 1924 else: 1925 ctx = context() 1926 executor_new = executor.new_executor(mode == ASYNC) 1927 executor_old = ctx.executor 1928 try: 1929 executor_old.wait() 1930 ctx.executor = executor_new 1931 yield 1932 finally: 1933 ctx.executor = executor_old 1934 executor_new.wait() 1935 1936 1937@tf_contextlib.contextmanager 1938def executor_scope(e): 1939 """Context manager for changing executor for current thread. 1940 1941 Args: 1942 e: A Executor to execute eager ops under this scope. Setting it to None will 1943 switch back to use the default executor for the context. 1944 1945 Yields: 1946 Context manager for setting the executor for current thread. 1947 """ 1948 ctx = context() 1949 executor_old = ctx.executor 1950 try: 1951 ctx.executor = e 1952 yield 1953 finally: 1954 ctx.executor = executor_old 1955 1956 1957@tf_export("experimental.function_executor_type") 1958@tf_contextlib.contextmanager 1959def function_executor_type(executor_type): 1960 """Context manager for setting the executor of eager defined functions. 1961 1962 Eager defined functions are functions decorated by tf.contrib.eager.defun. 1963 1964 Args: 1965 executor_type: a string for the name of the executor to be used to execute 1966 functions defined by tf.contrib.eager.defun. 1967 1968 Yields: 1969 Context manager for setting the executor of eager defined functions. 1970 """ 1971 current_options = context().function_call_options 1972 old_options = copy.copy(current_options) 1973 try: 1974 current_options.executor_type = executor_type 1975 yield 1976 finally: 1977 context().function_call_options = old_options 1978 1979 1980def is_async(): 1981 """Returns true if current thread is in async mode.""" 1982 return context().is_async() 1983 1984 1985def async_wait(): 1986 """Waits for ops dispatched in ASYNC mode to finish.""" 1987 return context().executor.wait() 1988 1989 1990def async_clear_error(): 1991 """Clears errors raised during ASYNC execution mode.""" 1992 return context().executor.clear_error() 1993 1994 1995def num_gpus(): 1996 """Get the number of available GPU devices. 1997 1998 Returns: 1999 The number of available GPU devices. 2000 """ 2001 return context().num_gpus() 2002 2003 2004def enable_run_metadata(): 2005 """Enables tracing of op execution via RunMetadata. 2006 2007 To retrieve the accumulated metadata call context.export_run_metadata() 2008 and to stop tracing call context.disable_run_metadata(). 2009 """ 2010 context().enable_run_metadata() 2011 2012 2013def disable_run_metadata(): 2014 """Disables tracing of op execution via RunMetadata.""" 2015 context().disable_run_metadata() 2016 2017 2018def enable_graph_collection(): 2019 """Enables graph collection of executed functions. 2020 2021 To retrieve the accumulated graphs call context.export_run_metadata() 2022 and to stop collecting graphs call context.disable_graph_collection(). 2023 """ 2024 context().enable_graph_collection() 2025 2026 2027def disable_graph_collection(): 2028 """Disables graph collection of executed functions.""" 2029 context().disable_graph_collection() 2030 2031 2032def export_run_metadata(): 2033 """Returns a RunMetadata proto with accumulated information. 2034 2035 The returned protocol buffer contains information since the most recent call 2036 to either enable_run_metadata or export_run_metadata. 2037 2038 Returns: 2039 A RunMetadata protocol buffer. 2040 """ 2041 return context().export_run_metadata() 2042 2043 2044@contextlib.contextmanager 2045def collect_graphs(optimized=True): 2046 """Collects a flat list of pre- or post-optimization graphs. 2047 2048 The collected graphs include device placements, which can be useful for 2049 testing. 2050 2051 Usage: 2052 2053 ``` 2054 @def_function.function 2055 def f(x): 2056 return x + constant_op.constant(1.) 2057 2058 with context.collect_graphs() as graphs: 2059 with ops.device("CPU:0"): 2060 f(constant_op.constant(1.)) 2061 2062 graph, = graphs # `graph` contains a single GraphDef for inspection 2063 ``` 2064 2065 Args: 2066 optimized: whether to collect optimized graphs or non-optimized graphs 2067 Yields: 2068 A list of GraphDefs, populated when the context manager exits. 2069 """ 2070 ctx = context() 2071 ctx.enable_graph_collection() 2072 try: 2073 graphs = [] 2074 yield graphs 2075 metadata = ctx.export_run_metadata() 2076 finally: 2077 ctx.disable_graph_collection() 2078 for graph in metadata.function_graphs: 2079 if optimized: 2080 graphs.append(graph.post_optimization_graph) 2081 else: 2082 graphs.append(graph.pre_optimization_graph) 2083 2084 2085def get_server_def(): 2086 return context().get_server_def() 2087 2088 2089def set_server_def(server_def): 2090 context().set_server_def(server_def) 2091 2092 2093def update_server_def(server_def): 2094 context().update_server_def(server_def) 2095 2096 2097def check_alive(worker_name): 2098 return context().check_alive(worker_name) 2099 2100 2101def add_function(fdef): 2102 """Add a function definition to the context.""" 2103 context().add_function(fdef) 2104 2105 2106def remove_function(name): 2107 """Remove a function from the context.""" 2108 context().remove_function(name) 2109 2110 2111# Not every user creates a Context via context.context() 2112# (for example, enable_eager_execution in python/framework/ops.py), 2113# but they do all import this file. Note that IS_IN_GRAPH_MODE and 2114# in_graph_mode are both parameterless functions. 2115def _tmp_in_graph_mode(): 2116 if context_safe() is None: 2117 # Context not yet initialized. Assume graph mode following the 2118 # default implementation in `is_in_graph_mode`. 2119 return True 2120 return not executing_eagerly() 2121 2122 2123is_in_graph_mode.IS_IN_GRAPH_MODE = _tmp_in_graph_mode 2124