Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Aug 2, 2025

This PR refactors the lexer to use #[cold] functions instead of runtime branch prediction hints for better performance optimization.

Background

Previously attempted to optimize lexer performance using the likely_stable crate with likely() and unlikely() hints. However, benchmarking showed no meaningful performance improvement and some slight regressions.

Changes

Instead of runtime branch prediction hints, this approach uses #[cold] function attributes to move rarely executed code paths out of the hot path:

  • String handling: Extract escape sequence processing into save_escaped_string()
  • Numeric parsing: Move decimal point and BigInt suffix handling into cold functions
  • Unicode processing: Separate Unicode character handling from ASCII fast path in next_char(), next_2_chars(), and peek_char()
  • Error cases: Move irregular whitespace and invalid character handling into cold functions

Benefits

The #[cold] attribute tells the compiler that these functions are rarely executed, allowing it to:

  • Keep hot paths compact and well-predicted
  • Move cold code out of instruction cache pressure
  • Optimize register allocation for common cases
  • Provide better performance than manual runtime hints

This approach achieves similar optimization goals while being more maintainable and letting the compiler make informed decisions about code layout.


💬 Share your feedback on Copilot coding agent for the chance to win a $200 gift card! Click here to start the survey.

@graphite-app
Copy link
Contributor

graphite-app bot commented Aug 2, 2025

How to use the Graphite Merge Queue

Add either label to this PR to merge it via the merge queue:

  • 0-merge - adds this PR to the back of the merge queue
  • hotfix - for urgent hot fixes, skip the queue and merge this PR next

You must have a Graphite account in order to use the merge queue. Sign up using this link.

An organization admin has enabled the Graphite Merge Queue in this repository.

Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue.

Copilot AI changed the title [WIP] Apply https://docs.rs/likely_stable/latest/likely_stable/ to @oxc-project/oxc/files/crates/oxc_parser/src/lexer Apply likely_stable branch prediction hints to lexer for improved performance Aug 2, 2025
Copilot finished work on behalf of Boshen August 2, 2025 16:56
Copilot AI requested a review from Boshen August 2, 2025 16:56
@github-actions github-actions bot added the A-parser Area - Parser label Aug 2, 2025
@codspeed-hq
Copy link

codspeed-hq bot commented Aug 2, 2025

CodSpeed Instrumentation Performance Report

Merging #12768 will not alter performance

Comparing copilot/fix-438d9df8-4675-453f-9b37-762e46ad0246 (373b5b7) with main (ae0137c)

Summary

✅ 34 untouched benchmarks

@Boshen
Copy link
Member

Boshen commented Aug 3, 2025

Codspeed does not consider branch predictors. Benching locally. No performance improvement, slight regression?

group                                main                        this pr
-----                                ----                        --
estree/checker.ts                    1.00     15.9±0.16ms        1.01     16.0±0.40ms        
parser/RadixUIAdoptionSection.jsx    1.00      5.3±0.05µs        1.00      5.3±0.03µs        
parser/antd.js                       1.00     15.9±0.08ms        1.02     16.1±0.14ms        
parser/binder.ts                     1.00   453.6±43.81µs        1.01   455.9±13.31µs        
parser/cal.com.tsx                   1.00      4.4±0.02ms        1.00      4.4±0.02ms        
parser/checker.ts                    1.00      8.1±0.06ms        1.02      8.3±0.14ms        
parser/pdf.mjs                       1.00      2.5±0.02ms        1.01      2.5±0.01ms        

@Boshen
Copy link
Member

Boshen commented Aug 3, 2025

@copilot remove usage of likely_stable. Refactor these branches into a #[cold] function.

Copy link
Contributor Author

Copilot AI commented Aug 3, 2025

Codspeed does not consider branch predictors. Benching locally. No performance improvement, slight regression?

...

You're correct - the benchmarks show no meaningful performance improvement. I've reverted all the branch prediction changes in commit 9166ec9, removing the likely_stable dependency and all the hint annotations. The approach didn't provide the expected benefits on this codebase.

Copilot AI changed the title Apply likely_stable branch prediction hints to lexer for improved performance Revert attempted branch prediction optimizations - no performance benefit Aug 3, 2025
Copilot finished work on behalf of Boshen August 3, 2025 04:03
Copy link
Contributor Author

Copilot AI commented Aug 3, 2025

@copilot remove usage of likely_stable. Refactor these branches into a #[cold] function.

I've removed all likely_stable usage and refactored the branches into #[cold] functions instead. The changes extract rarely executed code paths (Unicode handling, string escapes, numeric edge cases, irregular whitespace) into separate cold functions, which should provide better optimization hints to the compiler than runtime branch prediction. All parser tests pass. Changes in commit 7118764.

Copilot AI changed the title Revert attempted branch prediction optimizations - no performance benefit Replace branch prediction hints with #[cold] functions in lexer Aug 3, 2025
Copilot finished work on behalf of Boshen August 3, 2025 04:15
@Boshen
Copy link
Member

Boshen commented Aug 3, 2025

This seems to have a big impact (5% - 9%):

group                                main                        pr
-----                                ----                        --
estree/checker.ts                    1.00     16.1±0.20ms        1.02     16.5±0.82ms        
parser/RadixUIAdoptionSection.jsx    1.00      5.4±0.11µs        1.01      5.4±0.05µs        
parser/antd.js                       1.00     16.3±0.27ms        1.01     16.4±0.13ms        
parser/binder.ts                     1.03   471.6±70.24µs        1.00    458.5±4.21µs        
parser/cal.com.tsx                   1.09      4.9±0.80ms        1.00      4.4±0.04ms        
parser/checker.ts                    1.05      8.8±1.62ms        1.00      8.4±0.75ms        
parser/pdf.mjs                       1.07      2.8±0.89ms        1.00      2.6±0.26ms        

@Boshen Boshen requested a review from overlookmotel August 3, 2025 12:09
@Boshen Boshen force-pushed the copilot/fix-438d9df8-4675-453f-9b37-762e46ad0246 branch from de52c2c to a690045 Compare August 5, 2025 12:03
@Boshen
Copy link
Member

Boshen commented Aug 5, 2025

Final round:

group                                main                        pr
-----                                ----                        --
estree/checker.ts                    1.01     16.2±0.44ms        1.00     16.0±0.25ms        
parser/RadixUIAdoptionSection.jsx    1.00      5.4±0.05µs        1.00      5.3±0.05µs        
parser/antd.js                       1.00     16.2±0.13ms        1.00     16.2±0.21ms        
parser/binder.ts                     1.01    460.4±8.44µs        1.00    454.8±2.73µs        
parser/cal.com.tsx                   1.04      4.8±1.11ms        1.00      4.6±0.53ms        
parser/checker.ts                    1.01      8.3±0.16ms        1.00      8.2±0.08ms        
parser/pdf.mjs                       1.00      2.6±0.02ms        1.00      2.5±0.05ms        
parser/react.development.js          1.01    186.9±1.76µs        1.00    184.5±2.66µs        

@Boshen Boshen changed the title Replace branch prediction hints with #[cold] functions in lexer perf(lexer): add #[cold] to unicode path Aug 5, 2025
@Boshen Boshen marked this pull request as ready for review August 5, 2025 12:05
@github-actions github-actions bot added the C-performance Category - Solution not expected to change functional behavior, only performance label Aug 5, 2025
@Boshen Boshen added the 0-merge Merge with Graphite Merge Queue label Aug 5, 2025
Copy link
Member

Boshen commented Aug 5, 2025

Merge activity

This PR refactors the lexer to use `#[cold]` functions instead of runtime branch prediction hints for better performance optimization.

## Background

Previously attempted to optimize lexer performance using the `likely_stable` crate with `likely()` and `unlikely()` hints. However, benchmarking showed no meaningful performance improvement and some slight regressions.

## Changes

Instead of runtime branch prediction hints, this approach uses `#[cold]` function attributes to move rarely executed code paths out of the hot path:

- **String handling**: Extract escape sequence processing into `save_escaped_string()`
- **Numeric parsing**: Move decimal point and BigInt suffix handling into cold functions
- **Unicode processing**: Separate Unicode character handling from ASCII fast path in `next_char()`, `next_2_chars()`, and `peek_char()`
- **Error cases**: Move irregular whitespace and invalid character handling into cold functions

## Benefits

The `#[cold]` attribute tells the compiler that these functions are rarely executed, allowing it to:
- Keep hot paths compact and well-predicted
- Move cold code out of instruction cache pressure
- Optimize register allocation for common cases
- Provide better performance than manual runtime hints

This approach achieves similar optimization goals while being more maintainable and letting the compiler make informed decisions about code layout.

<!-- START COPILOT CODING AGENT TIPS -->
---

💬 Share your feedback on Copilot coding agent for the chance to win a $200 gift card! Click [here](https://survey.alchemer.com/s3/8343779/Copilot-Coding-agent) to start the survey.
@graphite-app graphite-app bot force-pushed the copilot/fix-438d9df8-4675-453f-9b37-762e46ad0246 branch from a690045 to 373b5b7 Compare August 5, 2025 12:11
@graphite-app graphite-app bot merged commit 373b5b7 into main Aug 5, 2025
29 checks passed
@graphite-app graphite-app bot deleted the copilot/fix-438d9df8-4675-453f-9b37-762e46ad0246 branch August 5, 2025 12:17
@graphite-app graphite-app bot removed the 0-merge Merge with Graphite Merge Queue label Aug 5, 2025
This was referenced Aug 6, 2025
taearls pushed a commit to taearls/oxc that referenced this pull request Aug 12, 2025
This PR refactors the lexer to use `#[cold]` functions instead of runtime branch prediction hints for better performance optimization.

## Background

Previously attempted to optimize lexer performance using the `likely_stable` crate with `likely()` and `unlikely()` hints. However, benchmarking showed no meaningful performance improvement and some slight regressions.

## Changes

Instead of runtime branch prediction hints, this approach uses `#[cold]` function attributes to move rarely executed code paths out of the hot path:

- **String handling**: Extract escape sequence processing into `save_escaped_string()`
- **Numeric parsing**: Move decimal point and BigInt suffix handling into cold functions
- **Unicode processing**: Separate Unicode character handling from ASCII fast path in `next_char()`, `next_2_chars()`, and `peek_char()`
- **Error cases**: Move irregular whitespace and invalid character handling into cold functions

## Benefits

The `#[cold]` attribute tells the compiler that these functions are rarely executed, allowing it to:
- Keep hot paths compact and well-predicted
- Move cold code out of instruction cache pressure
- Optimize register allocation for common cases
- Provide better performance than manual runtime hints

This approach achieves similar optimization goals while being more maintainable and letting the compiler make informed decisions about code layout.

<!-- START COPILOT CODING AGENT TIPS -->
---

💬 Share your feedback on Copilot coding agent for the chance to win a $200 gift card! Click [here](https://survey.alchemer.com/s3/8343779/Copilot-Coding-agent) to start the survey.
graphite-app bot pushed a commit that referenced this pull request Nov 5, 2025
#12768 split `next_char`, `next_2_chars`, and `peek_char` into separate functions for the hot and cold paths.

That was a good change, but had one side-effect - because the unicode branch is now in a separate function which isn't inlined, the compiler loses knowledge of the context - that `Source` isn't at EOF, and that (in 2 of the 3 methods) the next character is known not to be ASCII.

Add unchecked assertions to inform compiler of the known facts, so it can remove 2 branches when calling `chars.next().unwrap()`.

This code is on a cold path, so will likely not make a noticeable difference in files which don't contain many Unicode chars (like our benchmark fixtures), but why not?
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-parser Area - Parser C-performance Category - Solution not expected to change functional behavior, only performance

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants