使用性能测试 API#
本指南面向需要通过 Python 运行受支持的性能测试、读取带类型的结果,或添加性能测试数据生成器的自动化开发者。日常测试先参阅 运行性能测试 中的命令行工作流和结果解读。要单独测量某个资产方法、缓存属性或传感器更新,使用 编写微基准测试 。
运行一个工作流#
对受支持工作流使用带类型的请求。它采用与命令行相同的启动、任务、计时、数据结构和输出行为。以下运行时示例在预热 50 步后测量 1000 步,禁用可视化器,并同时输出稳定数据结构和便于阅读的摘要:
from pathlib import Path
from isaaclab.benchmark import (
BenchmarkLauncherConfig,
BenchmarkOutputConfig,
BenchmarkRuntimeRequest,
run_runtime_benchmark,
)
result = run_runtime_benchmark(
BenchmarkRuntimeRequest(
task="Isaac-Cartpole-Direct",
num_envs=4096,
num_steps=1000,
warmup_steps=50,
seed=42,
presets=("newton_mjwarp",),
output=BenchmarkOutputConfig(
path=Path("results/runtime"),
formatters=("schema", "summary"),
),
launcher=BenchmarkLauncherConfig(visualizers=()),
)
)
print(f"Total FPS: {result.bundle.runtime.total_fps.mean:,.0f}")
for output_path in result.output_paths:
print(output_path)
通过 Isaac Lab Python 包装脚本运行该文件:
./isaaclab.sh -p runtime_benchmark.py
命令打印摘要报告。 result.output_paths 中的路径指向已写入的数据结构 JSON 和摘要 JSON。自动化中应使用这些路径,而不是自行重建带时间戳的文件名。
当任务的 benchmark_mode 不为 None ,且设置 ISAACLAB_RENDER_PROFILE=1 或 ISAACLAB_PHYSICS_PROFILE=1 时,运行时工作流还会在 result.bundle.extra 中提供标量性能摘要: physics_mean_ms 、 physics_std_ms 、 physics_max_ms 、 physics_calls 及对应的 render_* 键。它们报告采样中每次调用的平均耗时、标准差、最大耗时 [ms] 和调用次数。禁用的范围不会生成对应键。这些摘要会写入 schema 和 OmniPerf 输出,数据结构版本仍为 1.4。按顺序保留的原始样本位于 <output_path>/profile_timings.json ,供本地分析使用。
选择请求#
根据要测量的问题,选择对应请求及专用运行器。如果通用分派更方便, run_benchmark() 可接受任意请求类型。
要回答的问题 |
请求 |
运行器 |
|---|---|---|
环境步进能力 |
||
应用与任务启动 |
||
强化学习训练与学习过程 |
||
已训练策略回放 |
以下示例复用运行时工作流的输出和启动器对象。
分析启动性能
启动性能分析记录每个阶段的实际耗时,以及耗时最多的函数:
from pathlib import Path
from isaaclab.benchmark import (
BenchmarkLauncherConfig,
BenchmarkOutputConfig,
BenchmarkStartupRequest,
run_startup_benchmark,
)
result = run_startup_benchmark(
BenchmarkStartupRequest(
task="Isaac-Cartpole-Direct",
num_envs=4096,
top_n=20,
presets=("newton_mjwarp",),
output=BenchmarkOutputConfig(
path=Path("results/startup"),
formatters=("schema", "summary"),
),
launcher=BenchmarkLauncherConfig(visualizers=()),
)
)
print(f"Environment creation: {result.bundle.phases['env_creation'].total_time_s:.3f} s")
运行训练
训练返回所选强化学习库生成的检查点:
from pathlib import Path
from isaaclab.benchmark import (
BenchmarkLauncherConfig,
BenchmarkOutputConfig,
BenchmarkTrainingRequest,
run_training_benchmark,
)
result = run_training_benchmark(
BenchmarkTrainingRequest(
backend="rsl_rl",
task="Isaac-Cartpole-Direct",
num_envs=4096,
max_iterations=500,
warmup_steps=50,
seed=42,
presets=("newton_mjwarp",),
output=BenchmarkOutputConfig(
path=Path("results/training"),
formatters=("schema", "summary"),
),
launcher=BenchmarkLauncherConfig(visualizers=()),
)
)
print(f"Collection FPS: {result.bundle.runtime.collection_fps.mean:,.0f}")
print(f"Checkpoint: {result.bundle.checkpoint_path}")
运行回放
在 新进程 中,将已保存的 checkpoint_path 传给回放请求:
from pathlib import Path
from isaaclab.benchmark import (
BenchmarkLauncherConfig,
BenchmarkOutputConfig,
BenchmarkPlayRequest,
run_play_benchmark,
)
checkpoint = Path("checkpoint.txt").read_text().strip()
result = run_play_benchmark(
BenchmarkPlayRequest(
backend="rsl_rl",
task="Isaac-Cartpole-Direct",
checkpoint=checkpoint,
num_envs=4096,
num_steps=1000,
warmup_steps=50,
seed=42,
presets=("newton_mjwarp",),
output=BenchmarkOutputConfig(
path=Path("results/play"),
formatters=("schema", "summary"),
),
launcher=BenchmarkLauncherConfig(visualizers=()),
)
)
print(f"Environment + inference FPS: {result.bundle.runtime.collection_fps.mean:,.0f}")
配置请求#
所有请求共享任务标识、可选环境数量和随机种子、任务配置、输出配置与启动器配置。
字段 |
用途 |
|---|---|
|
已注册的 Gym 任务标识,例如 |
|
环境数量与随机种子。比较时,两者都应保持固定。 |
|
带类型的任务预设,例如 |
|
额外的 Hydra 覆盖项,以字符串元组表示。 |
|
|
|
|
工作流程 |
字段 |
含义 |
|---|---|---|
运行时 |
|
测量的环境步数,以及之前不计入测量的步数。 |
启动 |
|
每个阶段保留的分析函数数量,以及可选过滤条件。 |
训练 |
|
强化学习库、学习迭代次数,以及不计入环境步计时的初始步数。 |
回放 |
|
强化学习库、策略、测量的推理步数,以及不计入测量的初始步数。 |
训练还提供学习、视频、传感器采集与收敛相关选项。训练和回放也提供 backend_args ,用于确实只属于某个强化学习库的选项。受支持选项应优先使用带类型的字段、预设和 hydra_args 。 backend_args 绕过通用请求约定。完整字段列表见 isaaclab.benchmark API 参考。
空的 visualizers 元组显式禁用全部可视化器; None 保留任务与环境的默认设置。启用相机、渲染、实时串流、确定性行为或动画录制会改变负载,比较的运行必须保持这些设置一致。
选择输出格式#
在 BenchmarkOutputConfig 中选择一个或多个格式化器名称:
格式化器 |
约定 |
|---|---|
|
写入稳定的带类型结果包,适合程序化比较和长期结果存储。 |
|
打印精简的终端报告,并写入扁平指标 JSON 文件。 |
|
写入更底层的阶段、测量与元数据表示。 |
|
为 Osmo 使用方写入按阶段组织的 KPI 文件。 |
|
写入 OmniPerf KPI 表示。 |
需要审阅或发布的结果应使用 ("schema", "summary") 。摘要提供即时反馈,schema 保留供分析使用的带类型数据。使用多个格式化器时,各输出文件名包含格式化器名称,因此 JSON 输出不会互相覆盖。
读取结果#
每个工作流都返回 BenchmarkResult ,包含两个成员:
bundle是RuntimeBundle、StartupBundle、TrainingBundle或PlayBundle。output_paths是所选格式化器实际写入的pathlib.Path对象元组。
应读取带类型的字段,而不是解析终端文本,或依赖 bundle.extra 中的键:
# Runtime or play
total_fps = result.bundle.runtime.total_fps.mean
step_fps = result.bundle.runtime.environment_step_timing.environment_step_fps.mean
# Startup
scene_creation_s = result.bundle.phases["env_creation"].total_time_s
# Training
collection_fps = result.bundle.runtime.collection_fps.mean
final_reward = result.bundle.learning.reward.final_raw
checkpoint = result.bundle.checkpoint_path
# Play, when at least one episode completed
mean_return = result.bundle.reward.mean if result.bundle.reward is not None else None
通用的 run 、 versions 和 hardware 字段保存比较所需的上下文。 runtime 区分采集吞吐量、总吞吐量、启动时间和环境步计时。测量定义见 运行性能测试 ;所有数据结构字段见公共 API 参考。
处理错误与进程生命周期#
如果工作流解析器拒绝带类型请求,API 抛出 ValueError ;如果工作流退出却没有返回结果,则抛出 RuntimeError 。无效输出配置也会报错,例如格式化器元组为空或包含未知名称。
每个工作流应在独立进程中运行,使各性能测试拥有完整、独立的仿真器生命周期,避免状态在工作流之间遗留,也与命令行执行模型一致。进程启动时间与稳态吞吐量分别报告。开始回放前,先保存 result.bundle.checkpoint_path 。
警告
measure_synchronized_step_breakdown=True 在环境与仿真步边界插入设备同步。这会使计算串行化,改变被测调度,Newton 尤其明显。该次运行的所有速率均属诊断数据,不能作为吞吐量报告。 SimulationContext.step() 之外的时间包含必要的动作、执行器、状态、管理器、重置、包装器和同步工作,不能直接视为可移除的 Isaac Lab 开销。
添加自定义数据生成器#
如果数据生成器的生命周期无法用受支持的工作流表示,可使用 BaseIsaacLabBenchmark 。框架提供阶段、带类型的测量与元数据记录、系统记录器和格式化器,但计时边界必须由你定义。
类型 |
用途 |
|---|---|
带有单位的单个标量,例如总时长或完成的操作数。 |
|
重复样本的均值、标准差、样本数和单位。 |
|
状态或正确性结果。 |
|
非标量样本的结构化测量数据。 |
|
保留的样本序列或学习序列。 |
|
|
比较测量结果所需的配置与身份信息。 |
以下可执行示例测量 CPU 负载,记录样本分布与配置,更新系统记录器,并确保最终完成性能测试的收尾:
import statistics
import time
from isaaclab.benchmark import (
BaseIsaacLabBenchmark,
IntMetadata,
StatisticalMeasurement,
)
num_iterations = 100
benchmark = BaseIsaacLabBenchmark(
benchmark_name="custom_cpu_workload",
formatter_type=["json", "summary"],
output_path="results/custom",
output_prefix="custom_cpu_workload",
use_recorders=True,
workflow_metadata={
"metadata": [{"name": "num_iterations", "data": num_iterations}]
},
)
output_paths = ()
try:
for _ in range(10):
sum(value * value for value in range(10_000))
samples_ms = []
for _ in range(num_iterations):
start_ns = time.perf_counter_ns()
sum(value * value for value in range(10_000))
samples_ms.append((time.perf_counter_ns() - start_ns) / 1.0e6)
benchmark.add_measurement(
"workload",
measurement=StatisticalMeasurement(
name="duration",
mean=statistics.fmean(samples_ms),
std=statistics.stdev(samples_ms),
n=len(samples_ms),
unit="ms",
),
metadata=IntMetadata(name="warmup_iterations", data=10),
)
benchmark.update_manual_recorders()
finally:
output_paths = benchmark.finalize()
for output_path in output_paths:
print(output_path)
选择有意义的阶段名,并明确单位。采样前先预热,报告均值与标准差时同时报告样本数。对于 GPU 计算,说明指标是提交延迟,还是设备同步后的完成延迟,并有意识地选择同步边界。
自定义数据生成器创建稳定工作流结果包时,应先调用 attach_bundle(bundle) ,再调用 finalize() ,并选择 schema 。否则,使用底层 json 表示。 finalize() 停止可选的 Kit 帧耗时记录器,收集记录器数据,写入所有选定格式,并返回写入路径。
use_recorders=True (默认值)时,基类采集 CPU、GPU、内存及版本元数据。在 finalize() 前至少调用一次 update_manual_recorders() ;长时间负载需要当前利用率样本时,也应在运行中更新。
只有 Kit 应用正在运行,且负载需要物理、渲染、应用或 GPU 帧耗时数据时,才设置 frametime_recorders=True 。框架启用可用的 Isaac Sim 性能测试服务,并跳过不可用记录器。帧耗时插桩本身也是负载的一部分,比较时应保持设置不变。
选择底层运行器#
MethodBenchmarkRunner 在不同输入模式、预热步数和实例数量下,重复运行某个方法或属性。使用 measure_latency() 定义成对的主机提交与设备同步计时边界;使用 LatencyBenchmarkRunner 报告这些结构化延迟样本。这些单项结果都不能预测端到端环境或训练吞吐量。命令矩阵与扩展协议见 编写微基准测试 。
故障排除#
通过 ./isaaclab.sh -p 运行脚本,确保 Isaac Lab 和仿真器 Python 环境已启用。如果缺少输出,检查返回的 output_paths ,确认自定义数据生成器调用了 finalize() 。格式化器名称使用小写且区分大小写,有效名称为 schema 、 summary 、 json 、 osmo 和 omniperf 。
缺少 GPU 元数据通常表示 nvidia-smi 或 CUDA 设备发现不可用。缺少 Kit 帧耗时测量,则可能是对应性能测试服务不存在,这并不影响非帧耗时阶段的有效性。
新增受支持的端到端工作流时,应保持带类型请求的构建、分派、schema 输出、摘要输出及命令行行为一致。对于单项操作,先参照 编写微基准测试 ,再添加新运行器或计时约定。