news 2026/9/17 11:50:48

Rerun Python SDK 日志管道基准测试实战:从吞吐量压测到微基准与火焰图剖析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Rerun Python SDK 日志管道基准测试实战:从吞吐量压测到微基准与火焰图剖析

Rerun Python SDK 日志管道基准测试实战:从吞吐量压测到微基准与火焰图剖析

【免费下载链接】rerunVisualize, query, and stream to train on multimodal robotics data.项目地址: https://gitcode.com/GitHub_Trending/re/rerun

导读

本文围绕 Rerun 仓库中 tests/python/log_benchmark/README.md 所定义的 Python SDK 日志(logging)管道基准测试展开,系统讲解如何通过pixi一键运行吞吐量(throughput)基准与单次调用开销(micro-benchmark),如何以py-spy生成火焰图定位热点,以及如何使用pytest-benchmark保存并对比两次改动前后的性能基线。读完本文,你将掌握 Rerun Python SDK 在本地进行性能回归检查与剖析的完整工作流,并能读懂测试用例背后的日志管道设计(行式rr.log、列式rr.send_columns、内存 sink 与 gRPC 直连等)。

这套基准测试是手动性能基准,不进入 CI,专门服务于本地 profiling 与回归检查——理解这一点,有助于你将它嵌入自己的开发流程,而不是等待 CI 结果。

基准测试的整体布局:三个文件各司其职

基准测试代码集中在tests/python/log_benchmark/目录下,由三个文件组成:

文件职责
tests/python/log_benchmark/init.py共享数据类Point3DInputTransform3DInput,负责用固定随机种子生成可复现的输入数据
tests/python/log_benchmark/test_log_benchmark.py吞吐量基准(throughput benchmarks):大数据批、逐点单条日志、图像、时间轴上的变换等
tests/python/log_benchmark/test_micro_benchmark.py微基准(per-call overhead):剖析rr.log()管道中每个环节的固定开销

数据类的设计值得先看一眼,因为它决定了测试的可复现性:

  • Point3DInput.prepare(seed, num_points)使用np.random.default_rng(seed),以固定种子生成positions(num_points, 3)的 float32)、colors(uint32)和radii(float32)数组,并附带默认 label"some label"
  • Transform3DInput.prepare(seed, num_entities, num_time_steps)生成形状为(num_time_steps, num_entities, 3)translations(取值[0, 10))和形状为(num_time_steps, num_entities, 3, 3)mat3x3s(取值[-1, 1))。

也就是说,同一套基准在任意机器、任意时间运行,输入数据都完全一致,性能差异只可能来自代码改动与环境差异——这正是做回归对比的前提。

通过 pixi 一键运行全部基准

项目使用 pixi 管理任务。在仓库根目录(rerun/)下,直接运行:

# 运行全部基准: pixi run py-bench # 只运行吞吐量基准(排除名称含 "micro" 的用例): pixi run py-bench -k "not micro" # 只运行微基准: pixi run py-bench -k micro # 运行某个具体基准,例如 Points3D 的微基准: pixi run py-bench -k "micro_log-Points3D"

-k是 pytest 的表达式过滤参数,这里py-bench命令会把-k之后的参数原样透传给 pytest。py-bench这个任务定义在 pixi.toml 中,其真实命令为:

py-bench = { cmd = "uvpy -m pytest --benchmark-only --benchmark-group-by=func --benchmark-sort=fullname", depends-on = ["py-build-release"] }

从中可以读出三条关键信息:

  1. 依赖py-build-release:运行基准前会先以 release 模式构建并安装 Python 绑定(maturin develop --release)。性能测试必须用 release 构建,否则 debug 构建的测量结果毫无意义。
  2. --benchmark-only:只执行被pytest-benchmark插件识别的基准函数(即带benchmarkfixture 的测试),跳过普通断言测试。
  3. --benchmark-group-by=func --benchmark-sort=fullname:结果按函数分组、按完整名称排序,便于在终端输出中按用例横向对比。

独立运行单个基准(配合性能分析器)

吞吐量基准的test_log_benchmark.py在文件底部提供了if __name__ == "__main__"独立入口,用于脱离 pytest 直接运行,方便挂接 py-spy、perf 等分析器。先进入 pixi shell:

pixi shell

然后直接运行:

# 独立运行 transform3d 吞吐量基准: uvpy -m tests.python.log_benchmark.test_log_benchmark transform3d # 带参数运行:10 个实体、10000 个时间步、按静态数据记录: uvpy -m tests.python.log_benchmark.test_log_benchmark transform3d --num-entities 10 --num-time-steps 10000 --static # 连接到一个正在运行的 Rerun viewer(先启动 `rerun`): uvpy -m tests.python.log_benchmark.test_log_benchmark transform3d --connect

这个独立入口支持如下参数(定义见 test_log_benchmark.py):

参数默认值说明
benchmark(位置参数)目前仅支持transform3d
--num-entities10实体数量
--num-time-steps1000时间步数量
--static关闭以静态数据记录(不随时间变化)
--connect关闭通过 gRPC 连接正在运行的 Rerun viewer,而不是写入内存 sink
--create-only关闭只构造Transform3D实例、不实际记录,用于分离“对象构造”与“日志管道”的开销

注意--create-only这个选项的妙用:把log_transform3d_translation_mat3x3(构造 + 记录)与create_transform3d_translation_mat3x3(仅构造)分开计时,从而精确区分 SDK 对象构造开销与底层日志/编码/传输开销。这也是吞吐量测试test_bench_transform3d_translation_mat3x3test_bench_create_transform3d_translation_mat3x3成对存在的原因。

运行时会打印实测吞吐量,例如:

Logged 100000 transforms in 3.21s (31153 transforms/second)

(数字仅为示意,实际取决于硬件、构建配置与记录目标。)

用 py-spy 生成火焰图

在独立运行模式下,可以方便地挂接采样式分析器 py-spy(不会像 cProfile 那样显著干扰被测代码):

# 生成火焰图(Linux 上建议加 --native 以包含原生栈帧): sudo PYTHONPATH=rerun_py/rerun_sdk:rerun_py py-spy record -o flamegraph.svg -- \ .venv/bin/python -m tests.python.log_benchmark.test_log_benchmark transform3d

要点说明:

  • PYTHONPATH=rerun_py/rerun_sdk:rerun_py让 Python 进程能导入仓库内尚未安装(或刚由py-build-release构建)的rerun包。
  • --native会采样 Rust 侧的原生栈帧,由于 Rerun 的 Python 绑定是基于 PyO3/maturin 的 Rust 实现,定位热点通常必须依赖原生栈信息。
  • 生成的flamegraph.svg用浏览器打开即可看到调用栈的横向火焰图,宽条即热点。Python 层与 Rust 原生层会分别体现,方便判断瓶颈在 SDK 封装、Arrow 编码还是底层存储/传输。

吞吐量基准详解:四种典型的日志形态

tests/python/log_benchmark/test_log_benchmark.py 中的吞吐量用例覆盖了四种典型的日志使用形态:

1. 单次调用写入超大批次(Points3D)

test_bench_points3d_large_batch固定参数num_points=50_000_000(5000 万点),一次性调用rr.log("large_batch", rr.Points3D(...)),测量单次调用承载超大数据量的能力,反映 Arrow 数组构建与传输的吞吐上限。

2. 海量小调用(逐点单条日志)

test_bench_points3d_many_individual固定num_points=100_000,在循环中逐点调用rr.log("single_point", rr.Points3D(...)),每次只带 1 个点。这是典型的“每帧逐实体记录”模式,测的是小消息场景下日志管道的消息处理速率。

3. 图像/Tensor 数据

test_bench_image固定参数为1024^2px-4channels-20000calls(1024×1024、4 通道、20000 次调用),循环记录同一张rr.Tensor(image)。注意它用np.zeros(...)构造全零图像,测量的是重复记录同尺寸张量的稳定吞吐。

4. 时间序列上的 Transform3D:行式 vs 列式

test_bench_transforms_over_time对同一批 10000 个变换,以num_transforms_per_batch为参数做了三档对比:1(纯行式)、1001000(列式批大小)。

  • 行式(batch=1):每个时间步调用rr.set_time("frame", sequence=i)rr.log(...),模拟逐帧逐实体记录;
  • 列式(batch>1):使用rr.send_columns一次性提交一个时间列和多个组件列,例如:
rr.send_columns( "test_transform", indexes=[rr.TimeColumn("frame", sequence=times)], columns=rr.Transform3D.columns( translation=rand_trans[start:end], quaternion=rand_quats[start:end], scale=rand_scales[start:end], ), )

rr.send_columns的语义在 rerun_py/rerun_sdk/rerun/_send_columns.py 中有明确说明:它与行式rr.log相对,接受多个等长列,每个索引处各列数据合并为一行逻辑记录;且该 API忽略通过rr.set_time设置的状态化时间,也不会自动注入log_tick/log_time默认时间轴。rr.TimeColumn要求sequencedurationtimestamp三选一且只能选一个(见 TimeColumn 实现),sequence对应整数序列(如帧号),duration对应相对时间,timestamp对应 Unix 绝对时间戳。这一组基准直接量化了“列式批量提交相对逐条set_time+log到底能快多少”,是选择日志策略的关键依据。

5. Transform3D + 双时间轴(吞吐与构造分离)

test_bench_transform3d_translation_mat3x3使用Transform3DInput(1000 实体 × 10000 时间步),对每条记录同时设置两个时间轴:frame(序列时间)与sim_timetime_index * 0.01的仿真时间);static=True参数变体则把整批数据作为静态数据记录(只记录一次,不随帧变化)。配合--static/--create-only独立入口,可以分别评估双时间轴场景下日志与对象构造各自的开销。

微基准详解:rr.log()管道的五层剖析

tests/python/log_benchmark/test_micro_benchmark.py 是理解 Python SDK 日志开销结构的最佳入口。它对四种 archetype(ScalarsPoints3DTransform3DBoxes3D)分别测量日志管道中的五个阶段:

基准函数测量环节说明
test_bench_micro_constructarchetype 对象构造rr.Scalars(42.0)rr.Points3D(...)等对象的纯 Python 构造开销
test_bench_micro_as_component_batches组件批次化调用archetype.as_component_batches(),把 archetype 拆成组件批次
test_bench_micro_log_components组件批量记录调用内部函数_log_components("test_entity", batches),进入日志管道的 Python 侧入口(rerun_py/rerun_sdk/rerun/_log.py)
test_bench_micro_log_arrow_msgArrow 消息直达原生层手动把组件批次转成 Arrow 数组后直接调用bindings.log_arrow_msg,测量绕过 archetype/批次化等 Python 封装后的原生层开销
test_bench_micro_log完整rr.log一次完整的rr.log("test_entity", archetype)调用(不包含对象构造,archetype 在基准外预先创建好)
test_bench_micro_set_time时间轴设置单独测量rr.set_time("frame", sequence=42)的开销

把同一 archetype 在这五个阶段的耗时横向对比,就能直观看出:开销究竟花在 Python 对象构造、组件序列化,还是原生 Rust 侧的编码/存储。例如某次测量中若log_arrow_msglog差距明显,说明瓶颈在 Python 侧的封装层;若差距很小,则说明瓶颈已经下探到原生层,优化方向应转向 Rust 侧。

基准对比:保存基线、改动、再对比

性能回归检查的核心流程是“改动前后各跑一次,再对比”。pytest-benchmark提供了结果持久化能力:

# 在当前分支上保存一份基线: pixi run py-bench -k micro --benchmark-save=before # 修改代码、重新构建(py-bench 依赖 py-build-release,会自动重编),再保存一份: pixi run py-bench -k micro --benchmark-save=after

保存的结果存放在仓库根目录下的.benchmarks/目录中,自动按序号命名,例如0001_before0002_after。对比时使用pytest-benchmark自带的 CLI:

uv run pytest-benchmark compare 0001 0002

compare会输出两份结果在均值、中位数、标准差、每轮耗时等维度上的逐用例对比。这里的uv run同样需要先处于 pixi 环境(pixi shell)中,因为pytest-benchmark是 Python 环境依赖。由于测试输入数据由固定随机种子生成(见__init__.pyprepare),两次运行的数据完全一致,因此对比结果可以归因于代码改动本身。

记录目标的选择:内存 sink 与 gRPC 直连的差异

基准代码中反复出现两个关键的 sink 选择,理解它们才能正确解读测量结果:

rr.memory_recording()(默认):将日志数据全部写入内存缓冲,而不是发送给 viewer。其实现见 rerun_py/rerun_sdk/rerun/_memory.py,本质是创建一个PyMemorySinkStorage,可后续通过MemoryRecording.num_msgs()drain_as_bytes()读取(drain_as_bytes会先冲刷当前 sink)。吞吐量基准每个函数开头都调用rr.memory_recording()以重置一个新的空内存 sink——这一步至关重要,它确保多次测量互不污染,避免把上一轮数据混入本轮。内存 sink 排除了网络传输与 viewer 渲染的干扰,测的是“日志进入 SDK 并完成编码存储”这一段的纯性能。

--connect/rr.connect_grpc():通过 gRPC 连接一个正在运行的 Rerun viewer(rr.connect_grpc的实现见 rerun_py/rerun_sdk/rerun/sinks.py,它会立即返回、异步发送数据)。这种模式包含了序列化、传输与 viewer 侧接收的完整链路,更接近生产环境,但由于引入了网络与 viewer 侧负载,测量噪声更大,更适合做端到端验证而非精确基准。

小结:一条完整的本地性能工作流

把这套基准工具串起来,就得到了一条可复用的本地性能检查工作流:

  1. 跑全量或定向基准pixi run py-bench -k "not micro"(吞吐)或pixi run py-bench -k micro(微基准),先看整体是否有明显异常;
  2. 定位热点:对疑似慢的用例,进入pixi shell后用uvpy -m tests.python.log_benchmark.test_log_benchmark transform3d独立运行,配合py-spy record --native生成火焰图,区分 Python 层与 Rust 原生层瓶颈;
  3. 隔离环节:利用--create-only、微基准的五个阶段、以及行式/列式对比用例,把开销精确归因到对象构造、批次化、Arrow 编码、时间轴设置或原生 sink 等具体环节;
  4. 回归对比:改动前--benchmark-save=before,改动后--benchmark-save=after,用uv run pytest-benchmark compare 0001 0002出具定量结论。

这套基准并非 CI 中的自动门禁,而是为开发者准备的一套手动剖析工具箱。仓库内相关的文档入口是 tests/python/log_benchmark/README.md,测试数据类与用例代码分别在init.py、test_log_benchmark.py 与 test_micro_benchmark.py,建议在修改rr.log相关实现后,用上述流程跑一遍前后对比再合入改动。

【免费下载链接】rerunVisualize, query, and stream to train on multimodal robotics data.项目地址: https://gitcode.com/GitHub_Trending/re/rerun

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/17 11:49:18

职场中的性别心理学:打破刻板印象-中国心理学会心理咨询师水平评价-心理咨询师培训机构-长春心理咨询师培训机构

职场中的性别心理学:打破刻板印象-中国心理学会心理咨询师水平评价-心理咨询师培训机构-长春心理咨询师培训机构尽管社会在性别平等方面取得了显著进步,但职场中的性别偏见仍然以显性或隐性的方式存在。"女性更情绪化""男性不适合做护理工…

作者头像 李华
网站建设 2026/9/17 11:48:15

DeepSeek API实战:文件处理与图表生成的多模态应用指南

简介:一份面向技术开发人员的DeepSeek多模态API实战指南,聚焦文件处理与图表生成两大核心场景,系统讲解如何通过API完成本地文件读取、格式转换、数据筛选与提取,以及折线图、柱状图、饼图等可视化图表的生成与自定义。文档共25页…

作者头像 李华
网站建设 2026/9/17 11:46:33

GoGoGo 开源项目教程

GoGoGo 开源项目教程 【免费下载链接】GoGoGo 一个基于 Android 调试 API 百度地图实现的虚拟定位工具,并且同时实现了一个可以自由移动的摇杆 项目地址: https://gitcode.com/GitHub_Trending/go/GoGoGo 项目介绍 GoGoGo 是一个高效、灵活的命令行工具&am…

作者头像 李华
网站建设 2026/9/17 11:43:25

MySQL报错Incorrect string value?一文搞懂字符集与编码转换

1. 报错现场与第一层解读:Incorrect string value到底在抱怨什么先还原一下最常见的报错现场。某个周五下午,你正在给业务表加一条新记录,SQL执行完还没来得及松口气,控制台甩回来一行红字:ERROR 1366 (HY000): Incorr…

作者头像 李华