Skip to content

[Bug] Forward fastcache dataclass fields read only in a dead qd.static branch through every caller.#807

Closed
duburcqa wants to merge 5 commits into
mainfrom
duburcqa/fix_fastcache_dead_static_branch_pruning
Closed

[Bug] Forward fastcache dataclass fields read only in a dead qd.static branch through every caller.#807
duburcqa wants to merge 5 commits into
mainfrom
duburcqa/fix_fastcache_dead_static_branch_pruning

Conversation

@duburcqa

@duburcqa duburcqa commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Brief Summary

fastcache=True fails to compile a kernel when a @qd.func/kernel dataclass field is read only inside a compile-time-dead qd.static branch and the enclosing struct is forwarded through more than one caller path. It surfaces as either an unbound __qd_... name or a QuadrantsSyntaxError: Missing argument ..., depending on which call the un-forwarded field lands on. Plain nested dataclasses (any depth) already work; the trigger is specifically the dead-static-branch field combined with the discovery-order dependence below.

Walkthrough

Root cause: a field read only inside a dead qd.static branch is still marked used under the callee's func id (shared across the callee's qd.static template instantiations) via the branch's live instantiation. The enforcing pass therefore forwards that field from every caller. But the discovery pass copied a callee's used set into a caller only at the instant of each recorded call, so a caller whose call site is walked before the live-branch instantiation is discovered never records the field. The enforcing pass then emits a forward the caller never bound.

Fix (_pruning.py, kernel.py): record each caller -> callee by-name forwarding as an edge during discovery, and once every callee used set is final (after pass 0, before the parent-prefix reduction) replay the edges to a fixpoint. Each caller's used set thus becomes a superset of every field it forwards. This closes over multi-hop call chains and arbitrary dataclass nesting depth.

The enforcing pass also consults a recorded map from each caller argument name to the callee parameter it binds, to decide which forwarded arguments to keep. Recording that map unconditionally - needed so a dead-branch call site still forwards the field - exposed a second issue: the map was shared per callee, so two calls binding the same flat name to different callee slots (used in one, unused in the other) overwrote each other, and the enforcing pass then pruned the field the first call actually needs. The map is now keyed per call site by (caller_func_id, callee_func_id, node.lineno, node.col_offset) (_pruning.py, call_transformer.py); the source position is stable across the two passes, so each call site records its own map, whether the two calls sit in different callers or repeat inside one caller.

All of the above is compile-time pruning logic in materialize; the generated kernel and its launch path are unchanged.

Tests

Regression tests in test_py_dataclass.py, each failing on the unfixed tree on both arch configs and passing after:

  • test_prune_used_parameters_fastcache_dead_static_branch - two caller paths forward the struct to a callee that reads the field only in a dead qd.static branch, with the dead-branch caller walked first.
  • test_prune_used_parameters_fastcache_dead_static_branch_nested - same, but the forwarded field lives three dataclass levels deep.
  • test_prune_used_parameters_fastcache_forward_same_name_swapped_slots - two callers forward the same flat name into swapped callee slots (used in one, unused in the other), pinning the per-call-site map keying across callers.
  • test_prune_used_parameters_fastcache_forward_same_name_swapped_slots_same_caller - the same swap across two call sites inside a single caller.

Full test_py_dataclass.py + fast_caching/ + test_kernel_impl_dataclass.py suites: 285 passed, 6 xfailed, 0 regressions.

…c branch through every caller.

A dataclass field read only inside a compile-time-dead qd.static branch is still marked used under
the callee's func id, which is shared across the callee's qd.static template instantiations, via the
branch's live instantiation. The enforcing pass then forwards that field from every caller, but the
discovery pass only copied a callee's used set into a caller at the instant of each recorded call. A
caller whose call site is walked before the live-branch instantiation is discovered therefore never
records the field, and the enforcing pass emits a forward the caller never bound - surfacing as an
unbound name or a "Missing argument" argument-count mismatch depending on where the shortfall lands.

Record each caller -> callee by-name forwarding as an edge during discovery and, once every callee
used set is final, replay the edges to a fixpoint so each caller's used set is a superset of every
field it forwards. This closes over multi-hop call chains and arbitrary dataclass nesting depth.

Regression tests test_prune_used_parameters_fastcache_dead_static_branch and its _nested variant fail
before this change on both arch configs and pass after.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f30b6f894b

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread python/quadrants/lang/_pruning.py
@github-actions

Copy link
Copy Markdown

@hughperkins

Copy link
Copy Markdown
Collaborator

Definitely needs Genesis benchmarks and unit tests on this.

@github-actions

Copy link
Copy Markdown

@duburcqa

Copy link
Copy Markdown
Contributor Author

Definitely needs Genesis benchmarks and unit tests on this.

Benchmarks? Why?

…used cases instead of one parametrized function.
@duburcqa

Copy link
Copy Markdown
Contributor Author

@codex review

@hughperkins

Copy link
Copy Markdown
Collaborator

Definitely needs Genesis benchmarks and unit tests on this.

Benchmarks? Why?

fastcache stuff tends to trash the benchmarks really often. I haven't looked at the code paths involved at all, but 9/10 of fastcache changes cause benchmark regressions. So let's check please.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 01bba3855f

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread python/quadrants/lang/_pruning.py
@hughperkins

Copy link
Copy Markdown
Collaborator

I got Opus 4.8 to review this, since fastcache stuff is incredibly sensitive. Its outputs are positive. See below.

It had nothing critical to say. It left one minor comment:

"The single-shot copy is now redundant. With the fixpoint replaying the same edges against a superset of the callee sets, the vars_to_unprune accumulation in record_after_call (lines 80, 98-99, 112-115) no longer affects the final result. I would not rip it out though - keeping it minimizes contact area and lowers risk, consistent with AGENTS.md. Worth a one-line comment noting it's now belt-and-suspenders, if anything."

Perhaps we can add a FIXME as agent suggests, but not remove vars_to_unprune for now, for the reasons AI gives. (I have not looked at the code (though I once wrote it 😅, I remember creating a variable with that name ), just running with AI's suggestion here).

==== start of AI output ===

What it actually is

Despite the 305-file diff vs your local main, that main is just 50 commits stale. Against origin/main this branch is a single commit (f30b6f894) - a focused bug fix in the fastcache parameter-pruning pass. Three files: _pruning.py (logic), kernel.py (one call site), test_py_dataclass.py (two regression tests).

The bug and the fix

The root cause is real and subtly stated correctly: used_vars_by_func_id is keyed per func id, which is shared across all qd.static template instantiations of a function. So a field read only in a compile-time-dead branch of one instantiation still lands in the callee's used set via the live instantiation. The enforcing pass (filter_call_args) forwards based on that func-id-wide union, so every caller must bind the field - but the old discovery pass only did a single-shot copy of the callee's used set at the instant of each call (record_after_call, lines 98-99). A caller walked before the live instantiation was discovered missed the field -> unbound name / "Missing argument".

The fix records every caller -> callee by-name forwarding as an edge and replays them to a fixpoint (propagate_used_to_fixpoint) once all callee sets are final, before the parent-prefix collapse. This is the correct shape for the problem.

Correctness - looks sound

  • No over-pruning regression. The fixpoint only adds caller_arg_name when callee_param_name in callee_used - the exact same predicate as the enforcing side (filter_call_args lines 184, 209-212). So it can only mark things the enforcing pass would actually forward; it never spuriously forwards. The over-approximation here is inherent to func-id-shared used sets, pre-existing; the fix just makes the caller-side binding consistent with what enforcing already assumes.
  • Termination. Monotone set growth with changed only set on real additions -> terminates. Multi-hop/nested chains close correctly.
  • Placement. Running in the _pass == 0 branch, before the prefix reduction (kernel.py:561), is right - the fixpoint operates on full __qd_ leaf names, then prefixes collapse. And it's correctly skipped on the fastcache load path (range_begin == 1), where used sets come from a cache that was itself written post-fix.

Test quality - strong

The 3x loop over a shared offline_cache_file_path is a nice touch: iter 0 materializes (fixpoint runs, cache written), iters 1-2 exercise the cache-load path. Both assert compute correctness (deep == 99) and pruning effectiveness (QD_ARRAY == 2, i.e. not_used still pruned). The nested variant confirms arbitrary dataclass depth. The path_dead-before-path_live ordering is deliberately the failing case.

Minor observations (none blocking)

  1. The single-shot copy is now redundant. With the fixpoint replaying the same edges against a superset of the callee sets, the vars_to_unprune accumulation in record_after_call (lines 80, 98-99, 112-115) no longer affects the final result. I would not rip it out though - keeping it minimizes contact area and lowers risk, consistent with AGENTS.md. Worth a one-line comment noting it's now belt-and-suspenders, if anything.
  2. Fixpoint is naive O(iterations x edges). Totally fine for kernel call graphs; a worklist would be premature optimization. Python here is the right level per AGENTS.md.
  3. callee_param_by_caller_arg_name_by_func_id now stores all __qd_ mappings unconditionally, not just used ones - negligible extra memory, and required for the deferred decision.
  4. No public API change, so no docs/ update needed. Contact area is well-contained.

Overall: tight, well-reasoned fix with a genuinely good regression test. The commit message and inline comments are unusually clear about the mechanism.

@duburcqa

Copy link
Copy Markdown
Contributor Author

fastcache stuff tends to trash the benchmarks really often. I haven't looked at the code paths involved at all, but 9/10 of fastcache changes cause benchmark regressions. So let's check please.

Fastcache is not used for performance mode, and we don't care much about perf for ndarray, useless it is completely trash of course.

@hughperkins

Copy link
Copy Markdown
Collaborator

Opus 4.8 states:

  • codex review comment identifies a real issue
  • this PR has now performance implications, since it all happesn at compile time
  • (but I would still prefer to run the genesis benchmarks and unit tests please; fastcache stuff is very sensitive I feel)

AI output:
Screenshot 2026-07-23 at 08 42 13

@github-actions

Copy link
Copy Markdown

@github-actions

Copy link
Copy Markdown

@github-actions

Copy link
Copy Markdown

@duburcqa

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. More of your lovely PRs please.

Reviewed commit: a7c7e37421

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@github-actions

Copy link
Copy Markdown

@duburcqa

duburcqa commented Jul 23, 2026

Copy link
Copy Markdown
Contributor Author

GS_ENABLE_NDARRAY=1 pytest -xsv -m benchmarks "tests/benchmarks/test_rigid.py"

This branch:

env=duck_in_box_hard 	| batch_size=30000 	| gjk_collision=False 	| dtype=ndarray 	| backend=cuda 	| compile_time=124.9 	| runtime_fps=9046557.0 	| realtime_factor=90465.6
env=go2 	| batch_size=4096 	| gjk_collision=True 	| dtype=ndarray 	| backend=cuda 	| compile_time=62.8 	| runtime_fps=2131330.0 	| realtime_factor=21313.3
env=franka_random 	| batch_size=30000 	| gjk_collision=True 	| dtype=ndarray 	| backend=cuda 	| compile_time=51.1 	| runtime_fps=8794908.0 	| realtime_factor=87949.1
env=box_pyramid_6 	| batch_size=4096 	| gjk_collision=False 	| dtype=ndarray 	| backend=cuda 	| compile_time=117.2 	| runtime_fps=67708.0 	| realtime_factor=677.1
env=duck_in_box_easy 	| batch_size=30000 	| gjk_collision=False 	| dtype=ndarray 	| backend=cuda 	| compile_time=121.3 	| runtime_fps=18024261.0 	| realtime_factor=180242.6
env=anymal_uniform_kinematic 	| batch_size=30000 	| dtype=ndarray 	| backend=cuda 	| compile_time=111.1 	| runtime_fps=7908759.0 	| realtime_factor=79087.6
env=franka 	| batch_size=30000 	| dtype=ndarray 	| backend=cuda 	| compile_time=111.7 	| runtime_fps=15431681.0 	| realtime_factor=154316.8
env=box_pyramid_5 	| batch_size=4096 	| dtype=ndarray 	| backend=cuda 	| compile_time=113.1 	| runtime_fps=159728.0 	| realtime_factor=1597.3
env=duck_in_box_hard 	| batch_size=30000 	| gjk_collision=True 	| dtype=ndarray 	| backend=cuda 	| compile_time=67.6 	| runtime_fps=2718396.0 	| realtime_factor=27184.0
env=anymal_uniform_kinematic 	| batch_size=0 	| dtype=ndarray 	| backend=cpu 	| compile_time=42.0 	| runtime_fps=1088.0 	| realtime_factor=10.9
env=franka_random 	| batch_size=30000 	| gjk_collision=False 	| dtype=ndarray 	| backend=cuda 	| compile_time=103.3 	| runtime_fps=12580065.0 	| realtime_factor=125800.7
env=box_pyramid_6 	| batch_size=4096 	| gjk_collision=True 	| dtype=ndarray 	| backend=cuda 	| compile_time=65.6 	| runtime_fps=66441.0 	| realtime_factor=664.4
env=anymal_random 	| batch_size=30000 	| dtype=ndarray 	| backend=cuda 	| compile_time=122.2 	| runtime_fps=7395955.0 	| realtime_factor=73959.6
env=go2 	| batch_size=4096 	| constraint_solver=Newton 	| gjk_collision=False 	| dtype=ndarray 	| backend=cuda 	| compile_time=106.8 	| runtime_fps=3428830.0 	| realtime_factor=34288.3
env=franka_random 	| batch_size=30000 	| constraint_solver=Newton 	| dtype=ndarray 	| backend=cuda 	| compile_time=107.6 	| runtime_fps=12800252.0 	| realtime_factor=128002.5
env=double_smplx 	| batch_size=4096 	| constraint_solver=Newton 	| dtype=ndarray 	| backend=cuda 	| compile_time=132.1 	| runtime_fps=10545.0 	| realtime_factor=52.7
env=duck_in_box_hard 	| batch_size=0 	| dtype=ndarray 	| backend=cpu 	| compile_time=52.4 	| runtime_fps=3398.0 	| realtime_factor=34.0
env=go2 	| batch_size=4096 	| constraint_solver=CG 	| gjk_collision=False 	| dtype=ndarray 	| backend=cuda 	| compile_time=104.4 	| runtime_fps=3046877.0 	| realtime_factor=30468.8
env=franka_random 	| batch_size=30000 	| constraint_solver=CG 	| dtype=ndarray 	| backend=cuda 	| compile_time=102.3 	| runtime_fps=12203676.0 	| realtime_factor=122036.8
env=g1_fall 	| batch_size=4096 	| constraint_solver=Newton 	| dtype=ndarray 	| backend=cuda 	| compile_time=102.1 	| runtime_fps=1348018.0 	| realtime_factor=6740.1
env=anymal_uniform 	| batch_size=30000 	| dtype=ndarray 	| backend=cuda 	| compile_time=120.8 	| runtime_fps=10064862.0 	| realtime_factor=100648.6
env=franka_accessors 	| batch_size=0 	| dtype=ndarray 	| backend=cpu 	| compile_time=35.7 	| runtime_fps=866.0 	| realtime_factor=8.7
env=franka_random 	| batch_size=0 	| dtype=ndarray 	| backend=cpu 	| compile_time=37.8 	| runtime_fps=3707.0 	| realtime_factor=37.1
env=shadow_hand_cubes 	| batch_size=0 	| dtype=ndarray 	| backend=cpu 	| compile_time=38.8 	| runtime_fps=195.0 	| realtime_factor=6.5
env=dex_hand 	| batch_size=4096 	| dtype=ndarray 	| backend=cuda 	| compile_time=131.7 	| runtime_fps=25377.0 	| realtime_factor=1586.1
env=anymal_zero 	| batch_size=30000 	| dtype=ndarray 	| backend=cuda 	| compile_time=131.4 	| runtime_fps=14295565.0 	| realtime_factor=142955.6
env=franka_accessors 	| batch_size=30000 	| dtype=ndarray 	| backend=cuda 	| compile_time=97.6 	| runtime_fps=10333660.0 	| realtime_factor=103336.6
env=box_pyramid_3 	| batch_size=4096 	| dtype=ndarray 	| backend=cuda 	| compile_time=100.6 	| runtime_fps=1176781.0 	| realtime_factor=11767.8
env=shadow_hand_cubes_sparse 	| batch_size=0 	| dtype=ndarray 	| backend=cpu 	| compile_time=40.6 	| runtime_fps=244.0 	| realtime_factor=8.1
env=duck_in_box_easy 	| batch_size=30000 	| gjk_collision=True 	| dtype=ndarray 	| backend=cuda 	| compile_time=67.1 	| runtime_fps=8138202.0 	| realtime_factor=81382.0
env=anymal_zero 	| batch_size=0 	| dtype=ndarray 	| backend=cpu 	| compile_time=37.1 	| runtime_fps=4191.0 	| realtime_factor=41.9
env=franka_free 	| batch_size=30000 	| dtype=ndarray 	| backend=cuda 	| compile_time=104.6 	| runtime_fps=20084622.0 	| realtime_factor=200846.2
env=box_pyramid_4 	| batch_size=4096 	| dtype=ndarray 	| backend=cuda 	| compile_time=115.3 	| runtime_fps=439412.0 	| realtime_factor=4394.1

main branch:

env=duck_in_box_easy 	| batch_size=30000 	| gjk_collision=False 	| dtype=ndarray 	| backend=cuda 	| compile_time=124.4 	| runtime_fps=18092983.0 	| realtime_factor=180929.8
env=anymal_uniform_kinematic 	| batch_size=30000 	| dtype=ndarray 	| backend=cuda 	| compile_time=115.5 	| runtime_fps=8027102.0 	| realtime_factor=80271.0
env=franka 	| batch_size=30000 	| dtype=ndarray 	| backend=cuda 	| compile_time=107.1 	| runtime_fps=15411136.0 	| realtime_factor=154111.4
env=box_pyramid_5 	| batch_size=4096 	| dtype=ndarray 	| backend=cuda 	| compile_time=105.3 	| runtime_fps=159321.0 	| realtime_factor=1593.2
env=duck_in_box_hard 	| batch_size=30000 	| gjk_collision=True 	| dtype=ndarray 	| backend=cuda 	| compile_time=68.9 	| runtime_fps=2643043.0 	| realtime_factor=26430.4
env=anymal_uniform_kinematic 	| batch_size=0 	| dtype=ndarray 	| backend=cpu 	| compile_time=39.4 	| runtime_fps=1071.0 	| realtime_factor=10.7
env=franka_random 	| batch_size=30000 	| gjk_collision=False 	| dtype=ndarray 	| backend=cuda 	| compile_time=108.9 	| runtime_fps=12457319.0 	| realtime_factor=124573.2
env=box_pyramid_6 	| batch_size=4096 	| gjk_collision=True 	| dtype=ndarray 	| backend=cuda 	| compile_time=66.4 	| runtime_fps=66076.0 	| realtime_factor=660.8
env=duck_in_box_hard 	| batch_size=30000 	| gjk_collision=False 	| dtype=ndarray 	| backend=cuda 	| compile_time=122.4 	| runtime_fps=9206246.0 	| realtime_factor=92062.5
env=go2 	| batch_size=4096 	| gjk_collision=True 	| dtype=ndarray 	| backend=cuda 	| compile_time=65.0 	| runtime_fps=2155357.0 	| realtime_factor=21553.6
env=franka_random 	| batch_size=30000 	| gjk_collision=True 	| dtype=ndarray 	| backend=cuda 	| compile_time=52.8 	| runtime_fps=8851173.0 	| realtime_factor=88511.7
env=box_pyramid_6 	| batch_size=4096 	| gjk_collision=False 	| dtype=ndarray 	| backend=cuda 	| compile_time=111.7 	| runtime_fps=68315.0 	| realtime_factor=683.1
env=anymal_random 	| batch_size=30000 	| dtype=ndarray 	| backend=cuda 	| compile_time=117.6 	| runtime_fps=7413764.0 	| realtime_factor=74137.6
env=go2 	| batch_size=4096 	| constraint_solver=Newton 	| gjk_collision=False 	| dtype=ndarray 	| backend=cuda 	| compile_time=111.3 	| runtime_fps=3427468.0 	| realtime_factor=34274.7
env=franka_random 	| batch_size=30000 	| constraint_solver=Newton 	| dtype=ndarray 	| backend=cuda 	| compile_time=107.8 	| runtime_fps=12778690.0 	| realtime_factor=127786.9
env=double_smplx 	| batch_size=4096 	| constraint_solver=Newton 	| dtype=ndarray 	| backend=cuda 	| compile_time=138.9 	| runtime_fps=10839.0 	| realtime_factor=54.2
env=duck_in_box_hard 	| batch_size=0 	| dtype=ndarray 	| backend=cpu 	| compile_time=52.1 	| runtime_fps=3364.0 	| realtime_factor=33.6
env=go2 	| batch_size=4096 	| constraint_solver=CG 	| gjk_collision=False 	| dtype=ndarray 	| backend=cuda 	| compile_time=105.8 	| runtime_fps=3063446.0 	| realtime_factor=30634.5
env=franka_random 	| batch_size=30000 	| constraint_solver=CG 	| dtype=ndarray 	| backend=cuda 	| compile_time=101.0 	| runtime_fps=12214497.0 	| realtime_factor=122145.0
env=g1_fall 	| batch_size=4096 	| constraint_solver=Newton 	| dtype=ndarray 	| backend=cuda 	| compile_time=107.2 	| runtime_fps=1348624.0 	| realtime_factor=6743.1
env=anymal_uniform 	| batch_size=30000 	| dtype=ndarray 	| backend=cuda 	| compile_time=131.3 	| runtime_fps=10097324.0 	| realtime_factor=100973.2
env=franka_accessors 	| batch_size=0 	| dtype=ndarray 	| backend=cpu 	| compile_time=36.5 	| runtime_fps=838.0 	| realtime_factor=8.4
env=franka_random 	| batch_size=0 	| dtype=ndarray 	| backend=cpu 	| compile_time=38.8 	| runtime_fps=3622.0 	| realtime_factor=36.2
env=shadow_hand_cubes 	| batch_size=0 	| dtype=ndarray 	| backend=cpu 	| compile_time=40.4 	| runtime_fps=193.0 	| realtime_factor=6.4
env=dex_hand 	| batch_size=4096 	| dtype=ndarray 	| backend=cuda 	| compile_time=128.0 	| runtime_fps=25411.0 	| realtime_factor=1588.2
env=anymal_zero 	| batch_size=30000 	| dtype=ndarray 	| backend=cuda 	| compile_time=128.2 	| runtime_fps=14305566.0 	| realtime_factor=143055.7
env=franka_accessors 	| batch_size=30000 	| dtype=ndarray 	| backend=cuda 	| compile_time=106.1 	| runtime_fps=10189486.0 	| realtime_factor=101894.9
env=box_pyramid_3 	| batch_size=4096 	| dtype=ndarray 	| backend=cuda 	| compile_time=109.5 	| runtime_fps=1172438.0 	| realtime_factor=11724.4
env=shadow_hand_cubes_sparse 	| batch_size=0 	| dtype=ndarray 	| backend=cpu 	| compile_time=38.8 	| runtime_fps=246.0 	| realtime_factor=8.2
env=duck_in_box_easy 	| batch_size=30000 	| gjk_collision=True 	| dtype=ndarray 	| backend=cuda 	| compile_time=68.6 	| runtime_fps=8218464.0 	| realtime_factor=82184.6
env=anymal_zero 	| batch_size=0 	| dtype=ndarray 	| backend=cpu 	| compile_time=36.3 	| runtime_fps=4172.0 	| realtime_factor=41.7
env=franka_free 	| batch_size=30000 	| dtype=ndarray 	| backend=cuda 	| compile_time=109.4 	| runtime_fps=20078863.0 	| realtime_factor=200788.6
env=box_pyramid_4 	| batch_size=4096 	| dtype=ndarray 	| backend=cuda 	| compile_time=116.5 	| runtime_fps=439282.0 	| realtime_factor=4392.8

@duburcqa

Copy link
Copy Markdown
Contributor Author
env batch_size backend gjk_collision constraint_solver main_fps branch_fps delta_fps fps_pct baseline_std std_pct sigma_of_delta
double_smplx 4096 cuda Newton 10839 10545 -294 -2.71 152 1.43 -1.37
duck_in_box_hard 30000 cuda False 9206246 9046557 -159689 -1.73 188511 2.01 -0.60
anymal_uniform_kinematic 30000 cuda 8027102 7908759 -118343 -1.47 154280 1.98 -0.54
go2 4096 cuda True 2155357 2131330 -24027 -1.11 15431 0.71 -1.10
duck_in_box_easy 30000 cuda True 8218464 8138202 -80262 -0.98 84679 1.03 -0.67
box_pyramid_6 4096 cuda False 68315 67708 -607 -0.89 881 1.26 -0.49
shadow_hand_cubes_sparse 0 cpu 246 244 -2 -0.81 2 0.82 -0.71
franka_random 30000 cuda True 8851173 8794908 -56265 -0.64 94918 1.05 -0.42
go2 4096 cuda False CG 3063446 3046877 -16569 -0.54 12007 0.39 -0.98
duck_in_box_easy 30000 cuda False 18092983 18024261 -68722 -0.38 141714 0.77 -0.34
anymal_uniform 30000 cuda 10097324 10064862 -32462 -0.32 58429 0.58 -0.39
anymal_random 30000 cuda 7413764 7395955 -17809 -0.24 129120 1.78 -0.10
dex_hand 4096 cuda 25411 25377 -34 -0.13 162 0.64 -0.15
franka_random 30000 cuda CG 12214497 12203676 -10821 -0.09 131903 1.06 -0.06
anymal_zero 30000 cuda 14305566 14295565 -10001 -0.07 172713 1.20 -0.04
g1_fall 4096 cuda Newton 1348624 1348018 -606 -0.04 6232 0.46 -0.07
franka_free 30000 cuda 20078863 20084622 5759 +0.03 187716 0.93 +0.02
box_pyramid_4 4096 cuda 439282 439412 130 +0.03 3400 0.77 +0.03
go2 4096 cuda False Newton 3427468 3428830 1362 +0.04 16873 0.50 +0.06
franka 30000 cuda 15411136 15431681 20545 +0.13 151404 0.96 +0.10
franka_random 30000 cuda Newton 12778690 12800252 21562 +0.17 125622 1.00 +0.12
box_pyramid_5 4096 cuda 159321 159728 407 +0.26 1406 0.86 +0.20
box_pyramid_3 4096 cuda 1172438 1176781 4343 +0.37 7623 0.65 +0.40
anymal_zero 0 cpu 4172 4191 19 +0.46 21 0.57 +0.64
box_pyramid_6 4096 cuda True 66076 66441 365 +0.55 529 0.78 +0.49
franka_random 30000 cuda False 12457319 12580065 122746 +0.99 96767 0.76 +0.90
duck_in_box_hard 0 cpu 3364 3398 34 +1.01 11 0.36 +2.19
shadow_hand_cubes 0 cpu 193 195 2 +1.04 5 2.69 +0.28
franka_accessors 30000 cuda 10189486 10333660 144174 +1.41 159513 1.63 +0.64
anymal_uniform_kinematic 0 cpu 1071 1088 17 +1.59 14 1.49 +0.86
franka_random 0 cpu 3622 3707 85 +2.35 61 1.90 +0.99
duck_in_box_hard 30000 cuda True 2643043 2718396 75353 +2.85 67525 2.40 +0.79
franka_accessors 0 cpu 838 866 28 +3.34 6 0.78 +3.30

@duburcqa

Copy link
Copy Markdown
Contributor Author

Interpretation

  • 31 of 33 configs sit within ±1 sigma. The comparison is performance-neutral, now confirmed against real variance rather than assumed.
  • Only 2 exceed 2 sigma, both CPU bs=0 microbenchmarks: franka_accessors (+3.30 sigma, +3.34%) and duck_in_box_hard (+2.19 sigma, +1.01%). Their sigma is large only because their measured std is tiny (0.78% and 0.36%); the absolute moves are 28 and 34 FPS. These are the least trustworthy configs for cross-run stability, so I would not read a real speedup into them.
  • Everything stays well under the report's own ±8% runtime gate, so nothing would flag.

Note: the std comes from the official campaign's baseline runs available here.

@duburcqa duburcqa closed this Jul 23, 2026
@github-actions

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants