TensorFlow 源码拆解

2 · Tensor、设备与内存:值是如何被搬运的

从 Python EagerTensor 到 C++ TensorBuffer、allocator 和 device placement

Tensor 不是一块裸内存

C++ tensorflow::Tensor 至少把四件事绑在一起:DataType、TensorShape、TensorBuffer 和分配器/所有权语义。Tensor::dtype() 与 shape() 是元数据,tensor<T>() / flat<T>() 才是类型化数据访问;复制 Tensor 通常复制的是 buffer 引用或触发写时分离策略,而不是每次都深拷贝全部元素。

Tensor
 ├─ TensorShape      rank + dims + element count
 ├─ DataType         float32 / int64 / resource / variant ...
 ├─ TensorBuffer     data pointer + ref count + allocation metadata
 └─ Device placement 该 buffer 当前属于哪个 device / memory space

tensorflow/core/framework/tensor.h 中的 TensorBuffer 使用引用计数,并提供 root_buffer()、内存大小和 allocation description 等接口。这样 runtime 可以在不暴露具体 allocator 的情况下做内存统计、别名判断、临时 buffer 和跨设备复制。

Python 包装的两种身份

tensorflow/python/framework/ops.py 同时处理:

  • SymbolicTensor:指向 Graph 中某个 Operation 的输出,拥有 op、graph、value_index,不能在 Python 中直接 .numpy();
  • Eager Tensor:由 C API handle 支撑,可在执行完成后 .numpy(),但仍携带 dtype、shape、device 和生命周期信息。

这两个对象共享很多 Python 运算符入口,却不能混用。tf.function tracing 的核心工作之一,就是把 eager 输入替换成 placeholder-like 的符号输入,使同一份 Python 代码可以生成 Graph。

设备放置的两步

  1. Python 侧的 tf.device、Context 策略和输入 Tensor 提供偏好;
  2. C++ runtime 基于 op 的 kernel 注册、设备可用性、colocation、软放置和复制策略决定实际执行位置。

“请求 GPU”不等于“最终一定在 GPU”:如果某个 dtype/op 没有 GPU kernel,soft placement 可能把它放到 CPU;如果输入来自不同设备,还会插入隐式 copy 或把 op 放到能满足约束的共同设备。

内存生命周期的真正边界

一个 Tensor 可能经历:Python handle → C API tensor handle → runtime Tensor → device buffer → output handle。异步 executor 下,Python 调用返回时 device kernel 可能还没完成;.numpy()、显式同步或依赖后续操作会形成等待点。EagerExecutor 用节点队列和状态记录 pending、finished、error,避免把每次 Python 调用都强制变成全局同步。

源码锚点

  • tensorflow/core/framework/tensor.h:Tensor、TensorBuffer、shape/dtype/data API。
  • tensorflow/core/framework/tensor_shape.h:维度、未知维度和 element count。
  • tensorflow/python/framework/ops.py:SymbolicTensor、eager tensor 的 Python 侧协议。
  • tensorflow/python/eager/context.py:设备列表、线程本地 device stack、placement policy。
  • tensorflow/core/common_runtime/eager/eager_executor.h:异步节点、等待和错误传播。
  • tensorflow/core/common_runtime/device_set.h:设备集合与选择相关抽象。

观察实验

tf.debugging.set_log_device_placement(True)
x = tf.ones([1024, 1024])
y = tf.ones([1024, 1024])
z = tf.matmul(x, y)
_ = z.numpy()  # 把异步执行结果拉回 Python

观察日志时,把“op 的 requested device”“实际 kernel device”和“数据搬运”分开看;三者不一定相同。

On this page