Skip to content

check.py: give the non-Windows perf ceiling its denominator term - #1121

Merged
youknowone merged 1 commit into
mainfrom
str
Aug 9, 2026
Merged

check.py: give the non-Windows perf ceiling its denominator term#1121
youknowone merged 1 commit into
mainfrom
str

Conversation

@youknowone

@youknowone youknowone commented Aug 9, 2026

Copy link
Copy Markdown
Owner

Summary

One commit: the non-Windows perf ceiling was missing the denominator term its
Windows counterpart already carries.

Fixes pyre/check.py (ubuntu-24.04)
cranelift synth/exception_traceback_loop_forms 36.3x > gate 36x.

check.py: buffer the non-Windows perf ceiling against denominator noise

The ceiling test is pyre_exec > baseline_exec * limit + compare_buffer. The
right-hand side amplifies the baseline's measurement error by limit, and
only the Windows path carried a term for it (2q * (1 + limit)); off Windows the
buffer was just the left-hand BENCH_COMPARE_BUFFER_S, a numerator term. A
baseline near EXEC_TIME_FLOOR_S is a difference of two medians each granular to
about one floor, so the budget it sets swings by limit * EXEC_TIME_FLOOR_S.

That term is added off Windows now. The effective ceiling becomes
limit * (1 + EXEC_TIME_FLOOR_S / baseline): the grace is floor / baseline,
real only where the denominator cannot be trusted and vanishing for a baseline
that is real work (≤1% at a 1s baseline). No per-fixture ceiling is changed and
no baseline is re-recorded.

Not a regression in pyre: exception_traceback_loop_forms read 35.6x on one
runner and 36.3–37.6x on others against an unchanged ~2.2–2.4s exec — a 0.06s
pypy denominator wobbling ±0.004s across the line. A retry-by-median run failed
too, so it does not clear itself.

Complementary to #1086, which refits per-fixture ceilings to twice the slowest
observed ratio and reasons about the effective bound as
limit + compare_buffer/exec_baseline; #1086 did not refit this fixture.

Verification

  • Gate arithmetic on the runners' own numbers: the 36.3x/37.1x readings pass with
    the added term, main's 35.6x verdict is unchanged, and a synthetic 2x
    regression still fails.
  • CI on this branch: pyre/check.py passes on ubuntu-24.04 and
    windows-latest
    . The macOS check.py job and CPython suite (gate) fail, and
    both are inherited from main — same two rows, verbatim, in main's own run
    at 0c3c4777ba9: test.test_pickletools PASS -> FAIL (errors=1) and
    test.test_re PASS -> FAIL (errors=303). Neither is a perf row; the perf suite
    itself is 410/410 on dynasm and 410/410 on cranelift in that same job. test_re
    is the defect jit: re-emit a cut-trace ref held only by a guard snapshot, or decline the cut #1122 fixes.
    ⚠ Note this run is not a demonstration of the fix: main's ubuntu check.py
    is green in the control too, so the intermittent ceiling failure simply did not
    recur. The argument rests on the arithmetic and the recorded failing readings.

Measured scope, and the one thing this loosens

Counted from the comparison table of the macOS check.py job on this branch
(377 fixtures timed against pypy):

ceilings armed (dynasm) 273
printed ~, gate not applied (baseline clamped to the floor) 104
armed and exec_baseline ≥ FLOOR_GATE_MIN_BASELINE_S (0.05s) 67
armed with a baseline below that ~206
armed with pypy raw ≤ 0.020s, i.e. exec within one floor of the clamp 69

The clamp boundary sits exactly at pypy raw 0.020s (max among ~ rows = min
among armed rows), which puts pypy startup at about 0.015s.

The added grace is floor / exec_baseline, so it is ≤10% only for the 67
fixtures whose baseline clears FLOOR_GATE_MIN_BASELINE_S, and it approaches
100% — an effective ceiling of roughly 2 × limit — for the ~69 sitting just
above the clamp. FLOOR_GATE_MIN_BASELINE_S currently guards only the floor
(check.py:2038); the ceiling path has no such requirement, so this widening
lands on fixtures whose ratio the same constant already declares unmeasurable.

I am not changing that here: the motivating fixture is in the trustworthy group
(exception_traceback_loop_forms, pypy raw 0.09s ⇒ exec ≈ 0.075s ⇒ grace
≈ 6.7%), and disarming or re-scoping ceilings on ~206 fixtures is a coverage
decision, not a bug fix. The coherent follow-up is to make the ceiling decline to
arm below FLOOR_GATE_MIN_BASELINE_S exactly as the floor does — flagged here so
the choice is explicit rather than implied by this patch.

Withdrawn from this PR: the two cut-trace commits

This PR originally also carried
jit: seed the cut-trace escape closure from post-cut guard snapshots and
jit: decline a snapshot root whose cone needs an extra inputarg. Both are
dropped#1122 fixes the same defect with a sounder design, and the two
should not race each other into the same function:

this PR (dropped) #1122
cone predicate has_no_side_effect() is_always_pure()
on a refused root keep the NONE mapping and compile cancel the compilation

has_no_side_effect() is the wrong boundary: the band runs to
_NOSIDEEFFECT_LAST and so admits every mutable load (GETFIELD_GC,
GETARRAYITEM_GC, GC_LOAD, RAW_LOAD) and every allocation (NEW*). Prefix
re-emission re-executes the definition at the cut, so a replayed load returns
memory as it is at the cut rather than the value the snapshot named, and a
replayed allocation mints a blank second object. is_always_pure() is the
correct predicate. Credit to the Codex parity review on this PR for naming it.

One thing does not carry over to #1122, and is left here for whoever picks it
up: #1122 still treats a pre-cut original inputarg as an unconditional leaf,
with the comment that phase 4 maps it to its pool constant. Phase 4 has a second
arm — it appends the value as an extra inputarg — and
patch_new_loop_to_load_virtualizable_fields requires the run after the red args
to be exactly the virtualizable's expansion, asserting i == len(inputargs)
(compile.py:458). Detail and a reproduction are in a comment on #1122.

🤖 Generated with Claude Code

@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@youknowone, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 11 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 540a8963-fa52-4eb9-bb4f-98263adb8c3b

📥 Commits

Reviewing files that changed from the base of the PR and between 0c3c477 and a2b1d2b.

📒 Files selected for processing (1)
  • pyre/check.py

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@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: c062cb1504

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread majit/majit-metainterp/src/history.rs Outdated
let Some(op) = self.ops.get((r.raw() - num_original_inputargs) as usize) else {
return false;
};
if !op.opcode.has_no_side_effect() {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Preserve snapshot values instead of re-executing reads

When a snapshot-only box comes from a mutable read such as GETFIELD_GC_I, this predicate admits it because has_no_side_effect() includes loads even though replay is not value-preserving. For example, if a field is read, changed before the cut, and the later guard snapshot retains the old box, the emitted prefix rereads the changed field; guard-failure resume then writes the new value into the frame instead of the captured value. Allocations have the same problem because replay produces a fresh, potentially uninitialized object. Thread these snapshot-only values through the cut's live input set rather than deciding replayability from side effects.

AGENTS.md reference: AGENTS.md:L231-L232

Useful? React with 👍 / 👎.

@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown

🤖 Codex parity review

Static analysis of this diff vs the local RPython/PyPy sources (commit a2b1d2b).
Updated: 2026-08-09T09:12:12.598Z

Files in the reviewed diff
pyre/check.py

1. Regressions to PyPy parity introduced by this patch

None.

2. Other mismatches introduced by this patch

None.

3. Pre-existing mismatches (already present before this patch)

None.

4. Structural adaptations

  • pyre/check.py:1981 ↔ rpython/translator/timing.py:11: Pyre’s added limit * EXEC_TIME_FLOOR_S denominator-error allowance is CI performance-gate policy; RPython’s timer only records translation-event durations and has no corresponding pass/fail ratio logic. This is an external Rust-project validation-harness adaptation, not interpreter/JIT semantics to port.

The ceiling comparison `pyre_exec > baseline_exec * limit + buffer` amplifies
the baseline's own measurement error by `limit`, but only the Windows path
carried a term for it (`2q * limit`); off Windows the buffer was just the
left-hand `BENCH_COMPARE_BUFFER_S`. A baseline near EXEC_TIME_FLOOR_S is a
difference of two medians granular to about one floor, so the budget it sets
swings by `limit * EXEC_TIME_FLOOR_S`. Add that term off Windows, making the
effective ceiling `limit * (1 + EXEC_TIME_FLOOR_S / baseline)`: the grace is
`floor / baseline`, real only near the floor and vanishing for a baseline that
is real work. exception_traceback_loop_forms read 35.6x on one runner and 37.1x
on another against an unchanged ~2.3s exec, a 0.06s baseline wobbling +-0.004s
across the 36x line.

Assisted-by: Claude

@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: 773ba81a07

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread majit/majit-metainterp/src/history.rs Outdated
Comment on lines +831 to +833
if inputarg_consts.get(r.raw() as usize).is_none() {
return false;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Preserve snapshot inputs instead of declining missing constants

When a snapshot-only pre-cut result depends on an original input that has no entry in inputarg_consts—the production shape observed in test.test_urlparse—returning false leaves that live snapshot slot mapped to OpRef::NONE; if the post-cut guard fails, resume therefore restores an uninitialized value rather than the captured input. Avoiding the virtualizable-layout assertion by retaining this corrupt fallback is a workaround rather than a correctness fix; the cut entry layout must carry the missing live value. The newly added test_cut_trace_from_declines_a_snapshot_root_needing_an_extra_inputarg is fresh evidence beyond the existing comment because it explicitly asserts the NONE fallback for this missing-constant path.

AGENTS.md reference: AGENTS.md:L252-L254

Useful? React with 👍 / 👎.

@youknowone youknowone changed the title Re-land the lost cut-trace snapshot fix, and give the posix perf ceiling its denominator term check.py: give the non-Windows perf ceiling its denominator term Aug 9, 2026
@youknowone
youknowone merged commit 1b31a5b into main Aug 9, 2026
15 of 17 checks passed
@youknowone
youknowone deleted the str branch August 9, 2026 14:37
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.

1 participant