Open-Source Wikis

/

TensorFlow

/

Systems

/

Eager execution

tensorflow/tensorflow

Eager execution

The default execution mode in TensorFlow 2. Each op runs immediately on the device, returning an EagerTensor. Eager runtime lives in tensorflow/core/common_runtime/eager/ (C++) and tensorflow/python/eager/ (Python).

Purpose

  • Run a TF op as soon as it's called, without building a graph first.
  • Match NumPy ergonomics: tensors have values, you can print them, you can use Python control flow.
  • Co-exist with graph mode via @tf.function, which traces eager calls into a FuncGraph.

Directory layout

tensorflow/core/common_runtime/eager/
├── context.{h,cc}              # EagerContext: the per-process eager state
├── execute.{h,cc}               # ExecuteOp: dispatch a single op
├── kernel_and_device.{h,cc}    # Cached (kernel, device) for an op invocation
├── eager_operation.{h,cc}      # EagerOperation: builder for one op call
├── tensor_handle.{h,cc}        # Handle to an eager tensor (may be local or remote)
├── execute_node.{h,cc}         # Async execution unit
├── core.{h,cc}, core_tensor_handle.{h,cc}
└── ...

tensorflow/python/eager/
├── context.py                   # tf.config.* and EagerContext binding
├── def_function.py              # tf.function (older path; mostly forwards to polymorphic_function)
├── polymorphic_function/        # The new tf.function implementation
├── function.py
├── execute.py                   # Calls into tfe_wrapper.cc
├── tape.py                      # GradientTape
├── pywrap_tensor.py
└── ...

Key abstractions

Type File Description
EagerContext tensorflow/core/common_runtime/eager/context.h Per-process eager state: device list, function library, executors.
EagerOperation tensorflow/core/common_runtime/eager/eager_operation.h Builder for one op call: name, attrs, input handles.
TensorHandle tensorflow/core/common_runtime/eager/tensor_handle.h Handle to an eager tensor (local CPU/GPU or remote).
KernelAndDevice tensorflow/core/common_runtime/eager/kernel_and_device.h Cached resolved kernel for (op, device, input dtypes).
EagerExecutor tensorflow/core/common_runtime/eager/eager_executor.h Per-thread async queue.
tape.GradientTape tensorflow/python/eager/tape.py Records ops for backprop.
tf.function tensorflow/python/eager/polymorphic_function/ Traces a Python function into a FuncGraph.

How a single eager op runs

sequenceDiagram
    participant Py as Python: tf.add(a, b)
    participant Gen as gen_math_ops.add
    participant Pybind as tfe_wrapper.cc
    participant Ctx as EagerContext
    participant KAD as KernelAndDevice cache
    participant Kernel as MatMulOp / AddOp / ... (OpKernel)

    Py->>Gen: gen_math_ops.add(a, b)
    Gen->>Pybind: TFE_Py_FastPathExecute("Add", ...)
    Pybind->>Ctx: Execute EagerOperation("Add", inputs)
    Ctx->>KAD: Look up cached (kernel, device)
    KAD-->>Ctx: cached entry
    Ctx->>Kernel: Compute(OpKernelContext)
    Kernel-->>Ctx: outputs (TensorHandle)
    Ctx-->>Py: EagerTensor wrapping the handle

The KernelAndDevice cache is critical for performance: each (op_name, dtypes, attrs, device) combo gets resolved once to a kernel pointer; subsequent calls bypass the registry lookup.

tf.function and eager interoperate

tf.function (tensorflow/python/eager/polymorphic_function/) traces a Python function by calling it with symbolic Tensors. Every op call inside the trace adds a node to a FuncGraph instead of executing. The traced graph is registered in the EagerContext's function library and called like any other op:

@tf.function
def f(x):
    return x * x + 1

f(2.0)   # First call: traces and runs
f(3.0)   # Second call: re-uses the traced graph (same input signature)
f(tf.zeros([3]))  # New signature: re-traces

The polymorphic-function machinery is responsible for caching ConcreteFunctions by input signature, handling captured variables, and wiring up the AutoGraph source rewriter (see features/tf-function-and-autograph).

Gradients via tape

tf.GradientTape (tensorflow/python/eager/tape.py + tensorflow/python/eager/backprop.py) records every op + inputs/outputs in a thread-local stack while it's "watching." On tape.gradient(loss, vars) it walks the recorded graph backwards, looking up gradient functions in @RegisterGradient and applying them.

Async execution

EagerExecutor provides asynchronous mode: ops are enqueued and run on a background thread. Errors flow back as deferred status. Most production code uses sync mode; the async mode is occasionally used for overlapping host work with GPU compute.

Remote eager

TensorHandle can be a remote handle (tensorflow/core/common_runtime/eager/remote_tensor_handle_data.{h,cc}). The eager runtime can transparently send/receive across hosts via RemoteEagerExecute RPCs (tensorflow/core/distributed_runtime/eager/). This underlies multi-host eager and TPU eager execution.

Integration points

  • Python frontend: tensorflow/python/eager/ and tfe_wrapper.cc. Almost every public op call goes through here.
  • Function library: EagerContext::FuncLibDef — the place where tf.function-traced graphs live.
  • Distributed: tensorflow/core/distributed_runtime/eager/ and tensorflow/python/distribute/.
  • Gradient tape: tensorflow/python/eager/tape.py.

Entry points for modification

  • Eager op dispatch — tensorflow/core/common_runtime/eager/execute.cc.
  • New tape behaviour — tensorflow/python/eager/tape.py and backprop.py.
  • tf.function semantics — tensorflow/python/eager/polymorphic_function/.
  • New eager extension (e.g. dtype, device) — generally requires both new kernel registrations and updates to EagerContext::FindFunctionByName / device list.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Eager execution – TensorFlow wiki | Factory