mirror of
https://github.com/aphrody-labs/oxc.git
synced 2026-10-14 22:29:17 +00:00
perf(minifier): consume nodes in minimize_statements in reverse order (#26258)
changes `minimize_statement`s to consume statement via a single `reverse()` + `pop()` pass instead of repeatedly calling `take_in` on each slot. potential pitfall: result is in forward order, old stmts are in reverse, thus we have to be careful in places where we want to take all of them | File | Arena allocs | Delta | Arena size | Delta | |---|---:|---:|---:|---:| | `checker.ts` | `144024 -> 77557` | `-66467` | `19.13 MB -> 17.52 MB` | `-1.61 MB` | | `App.tsx` | `15675 -> 9622` | `-6053` | `2.88 MB -> 2.74 MB` | `-0.14 MB` | | `RadixUIAdoptionSection.jsx` | `29 -> 16` | `-13` | `35.63 kB -> 35.63 kB` | `~0` | | `pdf.mjs` | `44801 -> 26527` | `-18274` | `6.31 MB -> 5.89 MB` | `-0.42 MB` | | `antd.js` | `351645 -> 222308` | `-129337` | `48.77 MB -> 45.76 MB` | `-3.01 MB` | | `binder.ts` | `6809 -> 4124` | `-2685` | `1.16 MB -> 1.10 MB` | `-0.06 MB` | | `kitchen-sink.tsx` | `64956 -> 34571` | `-30385` | `9.39 MB -> 8.74 MB` | `-0.65 MB` | --------- Co-authored-by: Dunqing <dengqing0821@gmail.com>
This commit is contained in:
1 parent
9a023379c6
commit
2ca7d29b7a
2 files changed
+43
-48
No files matched your search
@@ -1,4 +1,4 @@
|
||||
use std::{iter, ops::ControlFlow};
|
||||
use std::iter;
|
||||
|
||||
use crate::generated::ancestor::Ancestor;
|
||||
use oxc_allocator::{ArenaBox, ArenaVec, TakeIn};
|
||||
@@ -15,6 +15,8 @@ use crate::{TraverseCtx, is_terminated::IsTerminated, keep_var::KeepVar};
|
||||
|
||||
use super::PeepholeOptimizations;
|
||||
|
||||
type StatementIter<'a> = <ArenaVec<'a, Statement<'a>> as IntoIterator>::IntoIter;
|
||||
|
||||
/// `false` when dropping `stmt` produces a byte-identical AST — a `var`
|
||||
/// with no initializers, which `KeepVar` re-emits unchanged at the end of
|
||||
/// the block. Flagging such an identity drop as a real change would
|
||||
@@ -48,12 +50,11 @@ impl<'a> PeepholeOptimizations {
|
||||
/// ## MinimizeExitPoints:
|
||||
/// <https://github.com/google/closure-compiler/blob/v20240609/src/com/google/javascript/jscomp/MinimizeExitPoints.java>
|
||||
pub fn minimize_statements(stmts: &mut ArenaVec<'a, Statement<'a>>, ctx: &mut TraverseCtx<'a>) {
|
||||
let mut old_stmts = stmts.take_in(ctx);
|
||||
let mut old_stmts = stmts.take_in(ctx).into_iter();
|
||||
let mut is_control_flow_dead = false;
|
||||
let mut keep_var = KeepVar::new();
|
||||
let mut identity_drops = 0u32;
|
||||
for i in 0..old_stmts.len() {
|
||||
let stmt = old_stmts[i].take_in(ctx);
|
||||
while let Some(stmt) = old_stmts.next() {
|
||||
if is_control_flow_dead
|
||||
&& !stmt.is_module_declaration()
|
||||
&& !matches!(stmt.as_declaration(), Some(Declaration::FunctionDeclaration(_)))
|
||||
@@ -73,14 +74,15 @@ impl<'a> PeepholeOptimizations {
|
||||
}
|
||||
continue; // drop: `stmt` is intentionally not pushed into `stmts`.
|
||||
}
|
||||
if Self::minimize_statement(stmt, i, &mut old_stmts, stmts, ctx).is_break() {
|
||||
break;
|
||||
}
|
||||
Self::minimize_statement(stmt, &mut old_stmts, stmts, ctx);
|
||||
// A statement that never completes normally — a direct jump, a
|
||||
// kept block ending in a jump, an if/else or try/catch where
|
||||
// every branch jumps — makes the rest of the list unreachable.
|
||||
// https://github.com/rolldown/rolldown/issues/10184
|
||||
if !is_control_flow_dead && stmts.last().is_some_and(Statement::is_terminated) {
|
||||
if !is_control_flow_dead
|
||||
&& !old_stmts.as_slice().is_empty()
|
||||
&& stmts.last().is_some_and(Statement::is_terminated)
|
||||
{
|
||||
is_control_flow_dead = true;
|
||||
}
|
||||
}
|
||||
@@ -129,11 +131,10 @@ impl<'a> PeepholeOptimizations {
|
||||
|
||||
fn minimize_statement(
|
||||
stmt: Statement<'a>,
|
||||
i: usize,
|
||||
stmts: &mut ArenaVec<'a, Statement<'a>>,
|
||||
stmts: &mut StatementIter<'a>,
|
||||
result: &mut ArenaVec<'a, Statement<'a>>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) -> ControlFlow<()> {
|
||||
) {
|
||||
match stmt {
|
||||
Statement::EmptyStatement(_) => (),
|
||||
Statement::VariableDeclaration(var_decl) => {
|
||||
@@ -146,9 +147,7 @@ impl<'a> PeepholeOptimizations {
|
||||
Self::handle_switch_statement(switch_stmt, result, ctx);
|
||||
}
|
||||
Statement::IfStatement(if_stmt) => {
|
||||
if Self::handle_if_statement(i, stmts, if_stmt, result, ctx).is_break() {
|
||||
return ControlFlow::Break(());
|
||||
}
|
||||
Self::handle_if_statement(stmts, if_stmt, result, ctx);
|
||||
}
|
||||
Statement::ReturnStatement(ret_stmt) => {
|
||||
Self::handle_return_statement(ret_stmt, result, ctx);
|
||||
@@ -168,7 +167,6 @@ impl<'a> PeepholeOptimizations {
|
||||
Statement::BlockStatement(block_stmt) => Self::handle_block(result, block_stmt, ctx),
|
||||
stmt => result.push(stmt),
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
fn join_sequence(
|
||||
@@ -532,13 +530,12 @@ impl<'a> PeepholeOptimizations {
|
||||
}
|
||||
|
||||
fn handle_if_statement(
|
||||
i: usize,
|
||||
stmts: &mut ArenaVec<'a, Statement<'a>>,
|
||||
stmts: &mut StatementIter<'a>,
|
||||
mut if_stmt: ArenaBox<'a, IfStatement<'a>>,
|
||||
result: &mut ArenaVec<'a, Statement<'a>>,
|
||||
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) -> ControlFlow<()> {
|
||||
) {
|
||||
Self::substitute_single_use_symbol_in_statement(&mut if_stmt.test, result, ctx, false);
|
||||
|
||||
// "var a; if (a = b(), c) d;" => "var a = b(); if (c) d;"
|
||||
@@ -596,12 +593,10 @@ impl<'a> PeepholeOptimizations {
|
||||
//
|
||||
let can_move_branch_condition_outside_scope =
|
||||
!if_stmt.alternate.as_ref().is_some_and(Self::statement_cares_about_scope)
|
||||
&& !stmts.get(i + 1..).is_some_and(|stmts| {
|
||||
stmts.iter().any(Self::statement_cares_about_scope)
|
||||
});
|
||||
&& !stmts.as_slice().iter().any(Self::statement_cares_about_scope);
|
||||
|
||||
if can_move_branch_condition_outside_scope {
|
||||
let drained_stmts = stmts.drain(i + 1..);
|
||||
let drained_stmts = stmts.by_ref();
|
||||
let mut body = if let Some(alternate) = if_stmt.alternate.take() {
|
||||
ArenaVec::from_iter_in(iter::once(alternate).chain(drained_stmts), ctx)
|
||||
} else {
|
||||
@@ -630,7 +625,7 @@ impl<'a> PeepholeOptimizations {
|
||||
});
|
||||
result.push(if_stmt);
|
||||
ctx.notice_change();
|
||||
return ControlFlow::Break(());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -642,12 +637,12 @@ impl<'a> PeepholeOptimizations {
|
||||
// "if (a) return b; else if (c) return d; else return e;" => "if (a) return b; if (c) return d; return e;"
|
||||
ctx.notice_change();
|
||||
result.push(Statement::IfStatement(if_stmt));
|
||||
return Self::minimize_statement(stmt, i, stmts, result, ctx);
|
||||
Self::minimize_statement(stmt, stmts, result, ctx);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
result.push(Statement::IfStatement(if_stmt));
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
fn handle_return_statement(
|
||||
|
||||
@@ -3,22 +3,22 @@ checker.ts:
|
||||
sys allocs: 150
|
||||
sys reallocs: 5
|
||||
sys deallocs: 150
|
||||
sys alloc bytes: 14996600 # 15.00 MB
|
||||
sys alloc bytes: 14996632 # 15.00 MB
|
||||
sys peak growth: 10169560 # 10.17 MB
|
||||
arena allocs: 143446
|
||||
arena allocs: 77041
|
||||
arena reallocs: 28269
|
||||
arena size: 19097584 # 19.10 MB
|
||||
arena size: 17492768 # 17.49 MB
|
||||
|
||||
App.tsx:
|
||||
file size: 415342 # 415.34 kB
|
||||
sys allocs: 61
|
||||
sys reallocs: 8
|
||||
sys deallocs: 61
|
||||
sys alloc bytes: 2128555 # 2.13 MB
|
||||
sys alloc bytes: 2128563 # 2.13 MB
|
||||
sys peak growth: 1612427 # 1.61 MB
|
||||
arena allocs: 15643
|
||||
arena allocs: 9590
|
||||
arena reallocs: 2709
|
||||
arena size: 2879680 # 2.88 MB
|
||||
arena size: 2741920 # 2.74 MB
|
||||
|
||||
RadixUIAdoptionSection.jsx:
|
||||
file size: 2520 # 2.52 kB
|
||||
@@ -27,50 +27,50 @@ RadixUIAdoptionSection.jsx:
|
||||
sys deallocs: 21
|
||||
sys alloc bytes: 17400 # 17.40 kB
|
||||
sys peak growth: 11628 # 11.63 kB
|
||||
arena allocs: 29
|
||||
arena allocs: 16
|
||||
arena reallocs: 6
|
||||
arena size: 35632 # 35.63 kB
|
||||
arena size: 35224 # 35.22 kB
|
||||
|
||||
pdf.mjs:
|
||||
file size: 567296 # 567.30 kB
|
||||
sys allocs: 2427
|
||||
sys reallocs: 205
|
||||
sys deallocs: 2427
|
||||
sys alloc bytes: 5334247 # 5.33 MB
|
||||
sys peak growth: 3693355 # 3.69 MB
|
||||
arena allocs: 44749
|
||||
sys alloc bytes: 5316219 # 5.32 MB
|
||||
sys peak growth: 3693363 # 3.69 MB
|
||||
arena allocs: 26475
|
||||
arena reallocs: 7699
|
||||
arena size: 6304256 # 6.30 MB
|
||||
arena size: 5889456 # 5.89 MB
|
||||
|
||||
antd.js:
|
||||
file size: 6686316 # 6.69 MB
|
||||
sys allocs: 1038
|
||||
sys reallocs: 56
|
||||
sys deallocs: 1038
|
||||
sys alloc bytes: 29976031 # 29.98 MB
|
||||
sys peak growth: 19934725 # 19.93 MB
|
||||
arena allocs: 351149
|
||||
sys alloc bytes: 29976375 # 29.98 MB
|
||||
sys peak growth: 19934749 # 19.93 MB
|
||||
arena allocs: 221812
|
||||
arena reallocs: 75941
|
||||
arena size: 48738712 # 48.74 MB
|
||||
arena size: 45729992 # 45.73 MB
|
||||
|
||||
binder.ts:
|
||||
file size: 193077 # 193.08 kB
|
||||
sys allocs: 33
|
||||
sys reallocs: 1
|
||||
sys deallocs: 33
|
||||
sys alloc bytes: 963008 # 963.01 kB
|
||||
sys alloc bytes: 963032 # 963.03 kB
|
||||
sys peak growth: 657404 # 657.40 kB
|
||||
arena allocs: 6784
|
||||
arena allocs: 4099
|
||||
arena reallocs: 834
|
||||
arena size: 1162400 # 1.16 MB
|
||||
arena size: 1098864 # 1.10 MB
|
||||
|
||||
kitchen-sink.tsx:
|
||||
file size: 732898 # 732.90 kB
|
||||
sys allocs: 1854
|
||||
sys reallocs: 174
|
||||
sys deallocs: 1854
|
||||
sys alloc bytes: 5307512 # 5.31 MB
|
||||
sys peak growth: 3707425 # 3.71 MB
|
||||
arena allocs: 64833
|
||||
sys alloc bytes: 5304692 # 5.30 MB
|
||||
sys peak growth: 3707457 # 3.71 MB
|
||||
arena allocs: 34448
|
||||
arena reallocs: 12187
|
||||
arena size: 9385624 # 9.39 MB
|
||||
arena size: 8732520 # 8.73 MB
|
||||
Reference in new issue
Block a user