Skip to content

Task Proto

本页中的所有地址、字段号和偏移都适用于 libtpu-0.0.40-cp314 wheel 中的 libtpu.so(build-id 89edbbe81c5b328a958fe628a9f2207d)。该二进制文件剥离符号 —— 完整 C++ 符号存在,且 .text/.rodata/.lrodata VMA 等于文件偏移。其他版本会有所不同。

摘要

tensorflow.profiler.Task proto 是 xprof profiler 的逐 worker 设备元数据记录:每个 host/worker 一个消息,用于确定捕获了 profile(build provenance、host identity、启动命令行)、何时捕获(profile 窗口),以及 —— 对 device-trace timeline 最重要的部分 —— silicon 以什么时钟频率运行。它是一个扁平 proto3 消息,包含 18 个单一标量字段,逐字节从 descriptor pool FileDescriptorProto @ 0xbe999a0(路径 third_party/xprof/plugin/tensorboard_plugin_profile/protobuf/task.proto,package tensorflow.profiler)中切出。运行时它是 Map<uint32 task_id, Task> —— 每条记录以 worker ordinal 为键 —— 由 cheap-op gate xprof::XlaJfProfileCheapOps(Task const&) @ 0xf2ca280 读取。

18 个字段中有三个是时钟频率:tensor_core_freq_hz(f11)、sparse_core_freq_hz(f12)和 gtc_freq_hz(f13),全部为 uint64。它们是 riegeli Trace Container 上记录的 clock-domain conversion 的运行时配套常量:每个 device-plane XEvent 偏移都是 round(gtc · 1e9 / (clk << 4)),除数 clk 是 GTC 频率。本页提供这些常量;除法数学和 resync-aware 分段转换器位于 riegeli 页面。重新实现者必须先内化一个细节(详见内置与运行时时钟权威):libtpu 内的转换器不会读取 Task.gtc_freq_hz —— 它加载 .lrodata 0x1c60480 处的内置逐 DeviceType kHz 表。Task proto 的 *_freq_hz 字段是同一常量的 captured-profile 记录,暴露给事后工具。

Task proto 是身份与时钟,不是设备遥测。可与 tpu-telemetry proto 对比,后者是周期性设备状态快照(功耗、throttle、ECC、温度):telemetry 回答“芯片现在在做什么”;Task 回答“哪个 build、哪个 host、哪个 clock domain 产生了这个 profile”。profiler 通过 17 项 TaskEnvStatType 字典,将 Task 字段作为 XStat 暴露到 kTaskEnvPlaneName host plane;XPlane/XStat/TraceMe 页面负责 plane 组装机制,本页负责 Task→XStat 字段映射。值得注意的是,芯片的 PCI identity 和拓扑坐标不是标量 Task 字段 —— 芯片身份位于 device-trace container 中的 DeviceIdentifiers PCI 元组,拓扑则是自由形式 system_topology XStat 字符串。

对于重新实现,契约是:

  • 18 字段 Task proto schema —— 字段号 → 名称 → proto 类型 → 语义组,全部逐字节来自 descriptor pool,包括三个时钟频率字段 f11/f12/f13。
  • 时钟常量角色 —— 哪个字段馈入 device timebase(gtc_freq_hz)、哪个是实际转换器输入(内置 kHz 表,而非 proto 字段),以及由此得到的 trace wrap period。
  • 17 项 TaskEnvStatType XStat 字典 —— 从 str→enum init list @ 0x21c20f00 恢复的 Task→host-plane surface 映射。
  • 身份边界 —— 此 proto 中的 identity 是什么(host/build/command line),以及哪些位于别处(PCI chip identity、topology)。
Prototensorflow.profiler.Task —— proto3,18 个单一标量字段
DescriptorFileDescriptorProto @ 0xbe999a0(路径 …/protobuf/task.proto,417 字节 DescriptorProto
运行时表Task::_table_ @ 0x2164fed0Task_globals_ @ 0x22266028
容器Map<uint32 task_id, Task>(每个 worker 一条记录;TryEmplaceInternal @ 0xf2fa900
Consumerxprof::XlaJfProfileCheapOps(Task const&) @ 0xf2ca280(读取 clocks/version)
时钟字段f11 tensor_core_freq_hz,f12 sparse_core_freq_hz,f13 gtc_freq_hz(全部 uint64
时钟字段字符串tensor_core_freq_hz @ 0xbe99b16sparse_core_freq_hz @ 0xbe99b33gtc_freq_hz @ 0xbe99b50
实际转换器输入内置逐 DeviceType kHz 表 @ .lrodata 0x1c60480(步长 0x448int32 @+4
Host XStat 字典17 项 TaskEnvStatType,str→enum init list @ 0x21c20f00;plane kTaskEnvPlaneName @ 0x21c9e0e8

Task Proto Schema

目的

Task 消息是 profiler session 元数据:一个 Map<uint32 task_id, Task> 在每次 profiling session 中填充一次,每个 host/worker 一个 entry。uint32 键是 worker ordinal;值携带产生二进制的 build、运行它的 host、profiler 捕获的时间窗口、逐引擎时钟频率以及 host resource caps。map 类型由 MapEntryFuncs<uint32, Task, FieldType 13, FieldType 11> / Map<uint32, Task>::TryEmplaceInternal @ 0xf2fa900 符号逐字节确认 —— FieldType 13uint32 键,FieldType 11 是嵌入消息值。

字段映射

全部 18 个字段都是单一(label 1)proto3 标量,从 417 字节 DescriptorProtoFileDescriptorProto @ 0xbe999a0 内的 message_type[0],field-4 位于 +0x5e)中切出,并由 protoc --decode_raw 交叉检查。Proto 类型码:1=double3=int644=uint648=bool9=string13=uint32。每个字段名都已与 .rodata 逐字确认(见字段字符串锚点)。

f#名称类型分组含义
1changelistint64build构建该二进制所用的 source revision
2clean_buildboolbuild构建时 workspace 是 clean 还是 dirty
3build_timeint64buildunix build timestamp
4build_targetstringbuildbuild-target label(作为 version string)
5command_linestringhost启动命令行参数
6start_timeint64host进程启动时间
7task_addressstringhostworker BNS / network address
8profile_time_nsuint64windowprofile start wall-clock,ns
9profile_duration_msuint32windowprofile 长度,ms
10host_trace_leveluint32windowhost TraceMe verbosity level
11tensor_core_freq_hzuint64clockTensorCore cycle clock (Hz)
12sparse_core_freq_hzuint64clockSparseCore cycle clock (Hz)
13gtc_freq_hzuint64clockGlobal Time Counter clock (Hz) —— 时间基准除数
14peak_memory_usageuint64resourcehost RSS 峰值
15cpu_limitdoubleresourceBorg CPU limit
16cpu_usagedoubleresourceBorg CPU usage
17workspace_idstringbuildworkspace identifier
18snapshotint64buildworkspace snapshot id

五个语义组并未在 proto 中声明 —— 它们是字段集的自然分区,也与 host XStat 字典遵循的分组一致:build provenance(1、2、3、4、17、18)、host/task identity(5、6、7)、profile window(8、9、10)、clock rates(11、12、13)、resource caps(14、15、16)。

注意 —— 编号并不按组连续:workspace_id(17)和 snapshot(18)是追加在 resource caps 后面的 build-provenance 字段,通常说明这些字段是在后续 proto revision 中添加的。重新实现必须以字段为键,而不是以组内位置为键,因为 wire tag 是编号。

这里的 Identity 是什么,又不是什么

Task proto 的 identity 字段是 host/build identity,不是 chip identity

  • Host identitytask_address(f7,worker BNS/address)、command_line(f5)和 start_time(f6)。uint32 task_id map 键在一个 session 内区分 worker。
  • Build identitychangelist/build_time/build_target/clean_build/workspace_id/snapshot —— libtpu 二进制自身的 version provenance。

重新实现者可能会期待的芯片级 identity —— PCI vendor/device 元组、芯片坐标、device ordinal —— 不在此 proto 中:

陷阱 —— 芯片的 PCI identity 是device-trace container 中携带的 12 字节 asic_sw.proto.DeviceIdentifiers 元组(JfTrace.device_identifiers,字段 19),不是 Task 字段。fleet/topology 坐标暴露为自由形式 system_topology XStat 字符串(TaskEnvStatType 15),不是结构化 Task 字段。去 Task proto 中寻找芯片坐标或 device ordinal 的重新实现将一无所获 —— 它们分别属于 device-trace containerDeviceIdentifiers keying 和 host-plane topology XStat。

字段字符串锚点 {#field-string-anchors}

字段名连续位于序列化 descriptor blob(protodesc_cold section)中,紧跟在消息头之后。三个时钟频率字段是时间基准的主锚点,并已逐字节确认:

字段字符串descriptor addr
tensor_core_freq_hz0xbe99b16
sparse_core_freq_hz0xbe99b33
gtc_freq_hz0xbe99b50

完整名称串跨越 0xbe99a030xbe99b50Taskchangelist、…、gtc_freq_hz);路径字符串 …tensorboard_plugin_profile/protobuf/task.proto 和 package tensorflow.profiler 均逐字存在,将 FileDescriptorProto 锚定在 0xbe999a0


时钟频率字段与时间基准

目的

三个 uint64 时钟频率字段(f11/f12/f13)是 device-trace 流水线将原始硬件 tick 转换为墙钟皮秒所需的运行时时钟常量。每个引擎 —— TensorCore、SparseCore —— 都有自己的 cycle clock,用于将逐引擎 duration-cycle count 转换为时间;Global Time Counter 有第三个芯片范围时钟。消耗这些常量的转换数学riegeli Trace Container 负责;本节精确说明哪个常量扮演哪个角色,以及实际运行时值来自哪里。

角色

字段f#在时间基准中的角色
gtc_freq_hz13芯片范围 Global Time Counterdevice-timeline 除数:每个 device-plane XEvent 偏移都是 round(gtc · 1e9 / (gtc_freq_hz << 4))
tensor_core_freq_hz11TensorCore cycle clock将 TensorCore 逐事件 duration-cycle count 转换为时间
sparse_core_freq_hz12SparseCore cycle clock将 SparseCore 逐事件 duration-cycle count 转换为时间

gtc_freq_hz 的角色是核心:每个 16 字节 trace 包上的 TraceHeader.timestamp 是 ×16 定点的 GTC tick(低 4 位为小数),TpuXLineBuilder::AddEvent(GtcSpan) @ 0xf1df1e0 将其除以 gtc_freq · 16 以落到皮秒。两个 ×16 因子 —— tick 中的一个,以及除数 << 4 中的一个 —— 正好抵消。逐引擎 tensor_core_freq_hz/sparse_core_freq_hz 是从字段名推断出来,用于缩放编解码器解码出的逐 band duration-cycle counts;精确的逐子系统 subscriber 是哪个进行了除法,未端到端跟踪(精确逐 payload 绑定置信度 LOW;字段名称与类型为 CERTAIN)。

注意 —— GTC clock 使设备 timeline 在全局上保持一致。由于 GTC 是芯片范围且跨芯片同步(由 SetGtcConfiguration 编程),一个频率即可把每个核心和每个芯片的事件转换到单一墙钟轴。相对地,逐引擎 TensorCore/SparseCore clocks 只需要扩展一个已经由 GTC 定位的事件上的相对 duration。这就是为什么 GTC clock 是 offset placement 中的除数,而 engine clocks 只缩放 durations。

内置与运行时时钟权威 {#the-baked-vs-runtime-clock-authority}

二进制中有两个 GTC 频率来源,重新实现者必须理解 libtpu 内转换器实际使用哪一个:

  1. 运行时 Task.gtc_freq_hz(f13,uint64 Hz) —— 在 session time 捕获到 profile 中的 proto 字段。
  2. 内置逐 DeviceType 表,位于 .lrodata 0x1c60480(每个 DeviceType 步长 0x448int32 @+4 = GTC freq,单位 kHzint32 @+8 = timestamp bit-width 48/45/64)。值包括 700000 kHz(pxc/jellyfish-class,48-bit ts)、800000/833000 kHz(vxc/gxc 45-bit families)以及 1333000 kHz(一个 64-bit-ts jellyfish-class family)。

GtcSpanConverter 的 clock divisor 从内置逐 DeviceType kHz 表加载,而不是从 Task.gtc_freq_hz 加载。converter ctor @ 0xf2cb6e0 无条件执行 clk = table_base[DeviceType*0x448 + 4]AddEvent 中的除法是 gtc · 1e9 / (clk_khz << 4)。kHz 单位来自字节算术(gtc_x16=16 → 700000 kHz = 1428.571 ps,800000 = 1250.000 ps,833000 = 1200.480 ps,1333000 = 750.188 ps,全部匹配 ticks/(clk_khz·1000)·1e12)。Task proto 的 gtc_freq_hz 是同一常量的 captured-profile companion —— 时钟的线上记录,暴露给下游工具 —— 不是 in-binary converter 用来相除的值。

这对重新实现者有两方面影响:

  • 如果重新实现 in-libtpu device→XPlane conversion,请完全按照二进制做法,以 DeviceType ordinal 为键,从逐 DeviceType kHz 表驱动除法。读取 Task.gtc_freq_hz 并除以它(单位 Hz,而非 kHz)会因小数缩放偏离 16×,并且使用错误来源。
  • 如果重新实现只有 serialized profile 的事后 trace readerTask.gtc_freq_hz 就是你的权威 —— 内置表是 libtpu 内部数据,外部 reader 不可用。两者在数值上应一致(gtc_freq_hz Hz == clk_khz · 1000)。

捕获 profile 的运行时 Task.gtc_freq_hz 是否曾在 converter ctor 中覆盖内置 clk,观察到 —— ctor 加载内置表,视野中没有 runtime-Hz override 路径(INFERRED;*_freq_hz 字段可能仅为事后 reader 存在)。override 问题标记为 LOW confidence;CERTAIN 的是 in-libtpu converter 使用内置 kHz 表。

Trace-Wrap Period

同一时钟常量决定 GTC 的固定宽度计数器多久 wrap。给定内置表中的 timestamp bit-width w+8:48 / 45 / 64)和 GTC 频率:

text
wrap_period_s = 2^w / gtc_freq_hz
              = 2^48 / 700e6  ≈ 4.0e5 s  (pxc/vlc, 48-bit ts, 700 MHz)
              = 2^45 / 800e6  ≈ 4.4e4 s  (vfc/glc/gfc, 45-bit ts, 800 MHz)

解析长 trace 的重新实现必须考虑此 wrap;resync-aware 分段转换器(GtcSpanConverter::TimespanFromGtcSpan @ 0xf2cb7e0,由 riegeli 页面负责)通过在 {walltime_ns, gtc} 校准锚点上二分搜索来处理中途 GTC reset,正因为单一扁平除法无法承受 counter wrap 或 resync。


TaskEnvStatType XStat 字典 {#the-taskenvstattype-xstat-dictionary}

目的

profiler 不会在 timeline 上将 Task proto 作为 proto 暴露 —— 它将选定的 Task 字段(加上一些派生值)渲染为 kTaskEnvPlaneName host plane 上的 XStat(plane name @ 0x21c9e0e8,形式为 /host:<n>)。从 stat name 到 enum id 的映射是 17 项 TaskEnvStatType enum,从 str→enum init list @ 0x21c20f00 恢复。XPlane/XStat 页面负责 plane 及其 XStatMetadata 的构建方式;本字典是 Task 专属 surface。

字典

17 条记录,步长 0x18+0x10 处 enum = 值 1..17,字符串 {ptr@+0, len@+8},ptr 在加载时通过 R_X86_64_RELATIVE 重定位)。每个名称都已在 .rodata 中逐字确认。“← Task field”列是每个 stat 的 provenance:

EnumXStat name.rodata str← Task field / derivation
1build_changelist0x84d97b6changelist (f1)
2build_snapshot0x84e35fbsnapshot (f18)
3build_workspace_id0x86fd57bworkspace_id (f17)
4clean_build0x86f91d7clean_build (f2)
5build_time0x86bf23bbuild_time (f3)
6build_target0x8503333build_target (f4)
7command_line_args0x854efeecommand_line (f5)
8process_start_time0x86be930start_time (f6)
9task_bns0x8544af9task_address (f7)
10profile_start_time0x86beaa0profile_time_ns (f8)
11profile_stop_time0x86bec17profile_time_ns + profile_duration_ms (f8+f9)
12peak_memory_usage0x86d7111peak_memory_usage (f14)
13borg_cpu_limit0x84f9429cpu_limit (f15)
14borg_cpu_usage0x86d7123cpu_usage (f16)
15system_topology0x84b9c7ftopology coords(自由形式字符串,不是 Task 标量)
16compilation_task_info0x85db5e3compiler/version metadata
17profile_options0x85363f2请求的 profile options

该映射大多与 Task 字段 1:1,对三个派生/外部 entry 例外:profile_stop_time(11)是计算值(start + duration),system_topology(15)/ compilation_task_info(16)/ profile_options(17)来自 profiler session 中的其他位置,而不是标量 Task 字段。名称 1–14 是 Task 的逐字节精确投影;15–17 对绑定到某个来源为 HIGH-confidence(字符串逐字节精确,三个非 Task entry 的精确 producer 未跟踪)。

怪癖 —— 时钟频率字段 f11/f12/f13 不在这个 host-plane 字典中。clocks 馈入的是 device-plane timebase(GTC→ps 除法),不是 host Task-environment plane。期望在 host plane 上找到 gtc_freq_hz XStat 的重新实现会找不到 —— clocks 被 converter 以数值方式消耗,从不作为 host-plane attribute 暴露。host plane 只携带 identity/version/window/resource 事实。

查找机制

两个方向(name→enum 和 enum→name)都是构建一次并缓存的 absl::flat_hash_map

c
// GetTaskEnvStatTypeStr @0x1c8eb8c0 / FindTaskEnvStatType @0x1c8eba20
//   forward map (str->enum) @0x22579b30, reverse (enum->str) @0x22579b20, guard @0x22579b38
function BuildTaskEnvStatTypeMaps():                 // StrMap lambda @0x1c8ec5e0
    once (guard @0x22579b38):                         // call_once
        m = flat_hash_map(/*capacity*/ 17);            // mov $0x11,%edx @0x1c8ec770
        for (name, enum) in init_list @0x21c20f00:     // 17 records, stride 0x18
            m.insert(name -> enum);                    // ptr field relocated R_X86_64_RELATIVE
        forward = m; reverse = invert(m);

init list 的 string-pointer 字段在文件镜像中为 0;实际 .rodata 地址是加载时应用的 R_X86_64_RELATIVE addend。map capacity 170x11)硬编码在 0x1c8ec770,与 enum 基数匹配。


Task 元数据流水线

text
profiler session start


Map<uint32 task_id, Task>            (one Task per host/worker; TryEmplaceInternal @0xf2fa900)
   │  Task { changelist/build_*/snapshot/workspace_id     [build provenance]
   │         command_line/start_time/task_address          [host identity]
   │         profile_time_ns/profile_duration_ms/host_trace_level   [window]
   │         tensor_core_freq_hz/sparse_core_freq_hz/gtc_freq_hz     [clock rates]
   │         peak_memory_usage/cpu_limit/cpu_usage          [resource caps] }

   ├──────────────► host plane (kTaskEnvPlaneName)
   │   surfaced as XStats via TaskEnvStatType (17 entries):
   │     build_changelist … task_bns … profile_start/stop_time …
   │     system_topology · compilation_task_info · profile_options
   │   (NB: the *_freq_hz clocks are NOT surfaced here)

   └──────────────► device timebase  [riegeli-trace-container.md]
       gtc_freq_hz (f13) == the captured record of the GTC clock
          │  in-libtpu converter divides by the BAKED per-DeviceType kHz table @0x1c60480,
          │  not by this field — Task.gtc_freq_hz is the post-hoc-reader companion

       TpuXLineBuilder::AddEvent(GtcSpan) @0xf1df1e0
          device_offset_ps = round(gtc · 1e9 / (clk_khz << 4))

相关符号和偏移

符号地址作用
FileDescriptorProto (task.proto)0xbe999a0切出 18 字段映射的 417 字节 descriptor
Field-name run0xbe99a030xbe99b50Taskchangelist、…、gtc_freq_hz
Task::_table_0x2164fed0runtime reflection table
Task_globals_0x22266028proto default-instance globals
Map<uint32,Task>::TryEmplaceInternal0xf2fa900逐 worker map insert
MapEntryFuncs<uint32,Task,…>0xf2f8060map-entry serialization funcs(key=u32,value=Task)
XlaJfProfileCheapOps(Task const&)0xf2ca280读取 clocks/version 的 consumer
TaskEnvStatType init list0x21c20f0017 条 {str,enum} 记录,步长 0x18
kTaskEnvPlaneName0x21c9e0e8host Task-env plane name
GetTaskEnvStatTypeStr0x1c8eb8c0enum→string accessor
FindTaskEnvStatType0x1c8eba20string→enum accessor
TaskEnvStatType map guard / fwd / rev0x22579b38 / 0x22579b30 / 0x22579b20构建一次的 flat_hash_maps
Baked per-DeviceType GTC clk table0x1c60480 (.lrodata)步长 0x448+4 clk(kHz),+8 ts-width
GtcSpanConverter::ctor(DeviceType)0xf2cb6e0从内置表加载 clk —— 实际除数来源
TpuXLineBuilder::AddEvent(GtcSpan)0xf1df1e0消耗 clock 的 GTC→ps 除法

注意 —— Task proto 的填充点 —— profile 时写入 tensor_core_freq_hz/sparse_core_freq_hz/gtc_freq_hz、build/version 字段以及 topology 到 Task 的 host-side 函数 —— 未定位为单个 setter。consumer(XlaJfProfileCheapOps @ 0xf2ca280)会读取它;producer 可能是 host-side xprof session-info collector,不在这里跟踪的 device-side decode 路径中(gap,不是猜测)。


相关组件

组件关系
riegeli Trace Container消耗此 proto 记录的 GTC clock 常量;负责 round(gtc · 1e9 / (clk << 4)) 除法和 resync-aware 分段转换器
tpu-telemetry proto正交的设备状态快照(power/throttle/ECC/temps);Task 是 profiler identity+clocks,telemetry 是 live device metrics —— 不同栈
XPlane / XStat / TraceMe负责 17 项 TaskEnvStatType 字典将 Task 字段暴露到其上的 plane/XStatMetadata 组装
Profiling and Telemetry Overviewcapture→encode→decode→xplane 流水线;Task proto 是框定 captured profile 的逐 worker 元数据记录

交叉引用