Skip to content

perf(codegen): SWAR-skip boring byte runs in sourcemap line/column scan#24023

Merged
graphite-app[bot] merged 1 commit into
mainfrom
perf/codegen-sourcemap-swar
Jul 2, 2026
Merged

perf(codegen): SWAR-skip boring byte runs in sourcemap line/column scan#24023
graphite-app[bot] merged 1 commit into
mainfrom
perf/codegen-sourcemap-swar

Conversation

@Boshen

@Boshen Boshen commented Jul 2, 2026

Copy link
Copy Markdown
Member

What

update_generated_line_and_column (in oxc_codegen's sourcemap builder) scans every emitted byte once to track the generated line/column for source-map tokens. Profiling Codegen::build with source maps enabled shows it is the single hottest function (~20% of self-time) — it walks the output byte-by-byte with a match per byte.

The vast majority of output bytes are "boring": ASCII that is neither a line break nor part of a Unicode char, and that don't change the line count or the last-line ASCII flag. This skips runs of them 8 bytes at a time with a branchless SWAR test — the classic zero-byte-in-a-word trick for \n/\r plus a high-bit test for non-ASCII — and only falls back to the (unchanged) per-byte logic at the interesting bytes.

Why it's correct

The fast-forward only advances the cursor; it never mutates line/column/ASCII state. It skips exactly the bytes the old scalar path already handled with idx += 1 and no state change, so it is a provably-equivalent transformation and the emitted source map is byte-identical.

  • SWAR predicate exhaustively checked against a brute-force reference (a false positive only forces a harmless scalar fallback; false negatives — the dangerous case — are ruled out).
  • New unit tests cover long lines, \r\n / lone \r, embedded Unicode (Ö), and U+2028, including line breaks straddling the 8-byte word boundary.
  • Existing sourcemap.rs integration tests + the full oxc_codegen suite pass; no unsafe, all slice access bounds-guarded.

Performance

Interleaved A/B (sourcemap-enabled codegen, median-of-medians):

file delta
binder.ts ~-4.5%
react.development.js ~-2%
App.tsx ~-1 to -3%
kitchen-sink.tsx (short-token synthetic) ~flat

The win scales with the amount of long string/comment/line content (≥8 boring bytes per run); there is no effect when source maps are disabled.

🤖 Generated with Claude Code

@Boshen
Boshen requested a review from Dunqing as a code owner July 2, 2026 00:17
@github-actions github-actions Bot added the A-codegen Area - Code Generation label Jul 2, 2026
@codspeed-hq

codspeed-hq Bot commented Jul 2, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 57 untouched benchmarks
⏩ 14 skipped benchmarks1


Comparing perf/codegen-sourcemap-swar (35c1975) with main (269ce46)

Open in CodSpeed

Footnotes

  1. 14 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@Boshen
Boshen force-pushed the perf/codegen-sourcemap-swar branch from 7d17009 to 35c1975 Compare July 2, 2026 00:29
@Dunqing Dunqing added the run-monitor-oxc Add to a PR to dispatch oxc-project/monitor-oxc CI against it label Jul 2, 2026
@oxc-guard

oxc-guard Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

@oxc-guard oxc-guard Bot removed the run-monitor-oxc Add to a PR to dispatch oxc-project/monitor-oxc CI against it label Jul 2, 2026
@Boshen Boshen added the 0-merge Merge with Graphite Merge Queue label Jul 2, 2026

Boshen commented Jul 2, 2026

Copy link
Copy Markdown
Member Author

Merge activity

graphite-app Bot pushed a commit that referenced this pull request Jul 2, 2026
…an (#24023)

## What

`update_generated_line_and_column` (in `oxc_codegen`'s sourcemap builder) scans every emitted byte once to track the generated line/column for source-map tokens. Profiling `Codegen::build` with source maps enabled shows it is the **single hottest function** (~20% of self-time) — it walks the output byte-by-byte with a `match` per byte.

The vast majority of output bytes are "boring": ASCII that is neither a line break nor part of a Unicode char, and that don't change the line count or the last-line ASCII flag. This skips runs of them **8 bytes at a time** with a branchless SWAR test — the classic zero-byte-in-a-word trick for `\n`/`\r` plus a high-bit test for non-ASCII — and only falls back to the (unchanged) per-byte logic at the interesting bytes.

## Why it's correct

The fast-forward **only advances the cursor**; it never mutates line/column/ASCII state. It skips exactly the bytes the old scalar path already handled with `idx += 1` and no state change, so it is a provably-equivalent transformation and the emitted source map is byte-identical.

- SWAR predicate exhaustively checked against a brute-force reference (a false positive only forces a harmless scalar fallback; false negatives — the dangerous case — are ruled out).
- New unit tests cover long lines, `\r\n` / lone `\r`, embedded Unicode (`Ö`), and U+2028, **including line breaks straddling the 8-byte word boundary**.
- Existing `sourcemap.rs` integration tests + the full `oxc_codegen` suite pass; no `unsafe`, all slice access bounds-guarded.

## Performance

Interleaved A/B (sourcemap-enabled codegen, median-of-medians):

| file | delta |
| --- | --- |
| binder.ts | ~-4.5% |
| react.development.js | ~-2% |
| App.tsx | ~-1 to -3% |
| kitchen-sink.tsx (short-token synthetic) | ~flat |

The win scales with the amount of long string/comment/line content (≥8 boring bytes per run); there is no effect when source maps are disabled.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
@graphite-app
graphite-app Bot force-pushed the perf/codegen-sourcemap-swar branch from 35c1975 to 025ab4e Compare July 2, 2026 01:53
…an (#24023)

## What

`update_generated_line_and_column` (in `oxc_codegen`'s sourcemap builder) scans every emitted byte once to track the generated line/column for source-map tokens. Profiling `Codegen::build` with source maps enabled shows it is the **single hottest function** (~20% of self-time) — it walks the output byte-by-byte with a `match` per byte.

The vast majority of output bytes are "boring": ASCII that is neither a line break nor part of a Unicode char, and that don't change the line count or the last-line ASCII flag. This skips runs of them **8 bytes at a time** with a branchless SWAR test — the classic zero-byte-in-a-word trick for `\n`/`\r` plus a high-bit test for non-ASCII — and only falls back to the (unchanged) per-byte logic at the interesting bytes.

## Why it's correct

The fast-forward **only advances the cursor**; it never mutates line/column/ASCII state. It skips exactly the bytes the old scalar path already handled with `idx += 1` and no state change, so it is a provably-equivalent transformation and the emitted source map is byte-identical.

- SWAR predicate exhaustively checked against a brute-force reference (a false positive only forces a harmless scalar fallback; false negatives — the dangerous case — are ruled out).
- New unit tests cover long lines, `\r\n` / lone `\r`, embedded Unicode (`Ö`), and U+2028, **including line breaks straddling the 8-byte word boundary**.
- Existing `sourcemap.rs` integration tests + the full `oxc_codegen` suite pass; no `unsafe`, all slice access bounds-guarded.

## Performance

Interleaved A/B (sourcemap-enabled codegen, median-of-medians):

| file | delta |
| --- | --- |
| binder.ts | ~-4.5% |
| react.development.js | ~-2% |
| App.tsx | ~-1 to -3% |
| kitchen-sink.tsx (short-token synthetic) | ~flat |

The win scales with the amount of long string/comment/line content (≥8 boring bytes per run); there is no effect when source maps are disabled.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
@graphite-app
graphite-app Bot force-pushed the perf/codegen-sourcemap-swar branch from 025ab4e to 5d6d04a Compare July 2, 2026 01:58
@graphite-app
graphite-app Bot merged commit 5d6d04a into main Jul 2, 2026
29 checks passed
@graphite-app graphite-app Bot removed the 0-merge Merge with Graphite Merge Queue label Jul 2, 2026
@graphite-app
graphite-app Bot deleted the perf/codegen-sourcemap-swar branch July 2, 2026 02:01
camc314 pushed a commit that referenced this pull request Jul 3, 2026
…an (#24023)

## What

`update_generated_line_and_column` (in `oxc_codegen`'s sourcemap builder) scans every emitted byte once to track the generated line/column for source-map tokens. Profiling `Codegen::build` with source maps enabled shows it is the **single hottest function** (~20% of self-time) — it walks the output byte-by-byte with a `match` per byte.

The vast majority of output bytes are "boring": ASCII that is neither a line break nor part of a Unicode char, and that don't change the line count or the last-line ASCII flag. This skips runs of them **8 bytes at a time** with a branchless SWAR test — the classic zero-byte-in-a-word trick for `\n`/`\r` plus a high-bit test for non-ASCII — and only falls back to the (unchanged) per-byte logic at the interesting bytes.

## Why it's correct

The fast-forward **only advances the cursor**; it never mutates line/column/ASCII state. It skips exactly the bytes the old scalar path already handled with `idx += 1` and no state change, so it is a provably-equivalent transformation and the emitted source map is byte-identical.

- SWAR predicate exhaustively checked against a brute-force reference (a false positive only forces a harmless scalar fallback; false negatives — the dangerous case — are ruled out).
- New unit tests cover long lines, `\r\n` / lone `\r`, embedded Unicode (`Ö`), and U+2028, **including line breaks straddling the 8-byte word boundary**.
- Existing `sourcemap.rs` integration tests + the full `oxc_codegen` suite pass; no `unsafe`, all slice access bounds-guarded.

## Performance

Interleaved A/B (sourcemap-enabled codegen, median-of-medians):

| file | delta |
| --- | --- |
| binder.ts | ~-4.5% |
| react.development.js | ~-2% |
| App.tsx | ~-1 to -3% |
| kitchen-sink.tsx (short-token synthetic) | ~flat |

The win scales with the amount of long string/comment/line content (≥8 boring bytes per run); there is no effect when source maps are disabled.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Boshen added a commit that referenced this pull request Jul 6, 2026
### 🚀 Features

- 260425f semantic/examples: Include unresolved references (#24214)
(camc314)
- 2d9b0b3 minifier: Fold boolean-literal ternary branches in value
contexts (#24110) (Dunqing)
- 61fbf10 ast: Implement `ReplaceWith` on all AST types (#24013)
(overlookmotel)
- 7db7a29 allocator: Add `ReplaceWith` trait (#24012) (overlookmotel)
- 4eb074e mangler: Add `reserved` option for names that must not be
mangled (#24041) (Dunqing)
- 2e62012 data_structures: Add `StringExt` trait (#24006)
(overlookmotel)
- 60e7160 minifier: Drop side-effect-free IIFEs whose result is unused
(#23967) (Dunqing)
- 26dd9e2 ast: Add method to widen inherited enum ref to parent ref
(#23961) (overlookmotel)

### 🐛 Bug Fixes

- e8b50ee transformer: Clean up semantics for stripped TypeScript syntax
(#24180) (camc314)
- d966d0b react_compiler: Remove clippy allows (#24168) (Boshen)
- 854ef8d react_compiler: Compile generic functions instead of
over-bailing on type-param hoisting (#24158) (Boshen)
- 093586c react_compiler: Align memoization cache-slot allocation with
Babel (#24157) (Boshen)
- 09c8f59 react_compiler: Normalize snapshot fixture paths (#24142)
(camc314)
- f13df97 react_compiler: Drop stray empty statement from catch bindings
(#24133) (Boshen)
- cb2a505 react_compiler: Codegen destructuring reassignment targets
(#24131) (Boshen)
- b82c394 react_compiler: Propagate codegen invariants instead of
emitting empty bodies (#24128) (Boshen)
- 5771982 react_compiler: Render unchanged programs as source in fixture
snapshots (#24129) (Boshen)
- 4b16e1a transformer/async-to-generator: Preserve direct eval scope
flags (#24136) (camc314)
- 4e9194f react_compiler: Lower `delete obj.prop` to
Property/ComputedDelete (#24123) (Boshen)
- 0b25582 ast: Type binding node `typeAnnotation` as `TSTypeAnnotation |
null` (#23113) (Boshen)
- 018c0e5 transformer: Hoist lowered async declarations (#22770)
(camc314)
- 652fbaf mangler: Keep names of destructured exported bindings (#24036)
(Dunqing)
- e274415 minifier: Don't drop global calls that throw despite pure
arguments (#23917) (Dunqing)
- 59abb30 minifier: Only merge string literals in `try_fold_add` when
the inner operator is `+` (#23622) (Jerry Zhao)

### ⚡ Performance

- c5ca77b transformer: Avoid cloning refresh options (#24191) (camc314)
- bf1a151 react_compiler: Compile out debug printers (#24184) (Boshen)
- abb44a0 transformer: Build fixed object-rest arguments (#24190)
(camc314)
- a4db731 isolated_declarations: Use `ReplaceWith` instead of `TakeIn`
(#24016) (overlookmotel)
- ff10855 transformer: Use `ReplaceWith` instead of `TakeIn` (#24015)
(overlookmotel)
- bd49aff ecmascript: Avoid heap-allocating Math.min/max/imul operands
(#23941) (Lawrence Lin)
- e4b708b react_compiler: Skip compiled files before prefilters (#24171)
(Boshen)
- c59f2fe rust: Return impl ExactSizeIterator from slice-backed
accessors (#24144) (Boshen)
- 5d6d04a codegen: SWAR-skip boring byte runs in sourcemap line/column
scan (#24023) (Boshen)
- a55e0be traverse: Reduce string operations in `get_var_name_from_node`
(#24007) (overlookmotel)
- e6d48e1 transformer/nullish_coalescing: Move cold path into separate
function (#23989) (overlookmotel)
- c4e35b5 transformer/object_rest_spread: Pre-allocate capacity in `Vec`
(#23988) (overlookmotel)
- 527b8e5 transformer/decorators: Narrow type earlier (#23987)
(overlookmotel)

### 📚 Documentation

- 30d17f5 allocator: Clarify docs for `TakeIn::take_in_box` (#24093)
(overlookmotel)
- 675e6a8 ast: Correct doc comment for `PrivateFieldExpression` (#24008)
(overlookmotel)
- e4c30e6 minifier: Explain what `dce` mode means (#23994) (Dunqing)
- 37cbf88 ast_macros: Document fields of `StructDetails` (#23959)
(overlookmotel)
- 4de3e54 ast: Correct doc comment (#23948) (overlookmotel)

Co-authored-by: Boshen <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-codegen Area - Code Generation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants