Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions benchmarks/app-patterns/results/matrix-20260521-224923.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# App-pattern bench matrix — 2026-05-21 22:49:23

Each cell is mean ms (min ms). Lower is better.
Final column is **perry/bun ratio** — > 2× is a real gap, < 1.5× is fine.

| Kernel | Perry | Bun | Node | perry/bun | perry/node |
|---|---:|---:|---:|---:|---:|
| buffer_transcode | 61.1 (59.2) | 43.9 (42.3) | 128.4 (105.0) | 1.39x ✓ ok | 0.48x |
| date_format_parse | 35.9 (34.5) | 47.0 (45.7) | 127.2 (124.9) | 0.76x ✅ win | 0.28x |
| json_parse_1mb | 84.5 (77.7) | 66.7 (64.9) | 144.3 (138.9) | 1.27x ✓ ok | 0.59x |
| json_stringify_1mb | 39.4 (37.6) | 36.6 (34.3) | 108.5 (106.1) | 1.08x ✓ ok | 0.36x |
| map_1m | 246.4 (207.5) | 286.0 (264.6) | 332.7 (282.9) | 0.86x ✅ win | 0.74x |
| object_deep_clone | **COMPILE FAIL** | – | – | – | – |
| promise_all_chains | 54.9 (53.8) | 31.1 (30.0) | 68.5 (66.8) | 1.77x ⚠ borderline | 0.80x |
| regex_replace | 45.9 (44.7) | 51.8 (50.1) | 110.1 (106.7) | 0.89x ✅ win | 0.42x |
| string_concat_csv | 42.1 (40.1) | 28.0 (25.4) | 96.4 (93.3) | 1.50x ⚠ borderline | 0.44x |
| string_split_map_join | 40.4 (39.0) | 46.0 (43.2) | 89.1 (86.9) | 0.88x ✅ win | 0.45x |
| string_template_interp | 55.1 (53.2) | 47.5 (43.2) | 118.6 (111.6) | 1.16x ✓ ok | 0.46x |

## Summary

- Perry-faster-than-Bun kernels: **4**
- Borderline (1.5–2× slower): **2**
- Slow (≥ 2× slower): **0**

**Priority backlog:** the ✗ slow rows are the gaps. Each one is a focused workstream.
26 changes: 26 additions & 0 deletions benchmarks/app-patterns/results/matrix-20260521-225643.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# App-pattern bench matrix — 2026-05-21 22:56:43

Each cell is mean ms (min ms). Lower is better.
Final column is **perry/bun ratio** — > 2× is a real gap, < 1.5× is fine.

| Kernel | Perry | Bun | Node | perry/bun | perry/node |
|---|---:|---:|---:|---:|---:|
| buffer_transcode | 83.2 (63.0) | 74.8 (62.3) | 232.1 (204.7) | 1.11x ✓ ok | 0.36x |
| date_format_parse | 51.3 (43.5) | 69.9 (59.7) | 271.6 (212.8) | 0.73x ✅ win | 0.19x |
| json_parse_1mb | 128.0 (107.7) | 139.9 (106.1) | 279.9 (221.5) | 0.91x ✅ win | 0.46x |
| json_stringify_1mb | 50.2 (40.2) | 68.5 (47.9) | 204.9 (174.3) | 0.73x ✅ win | 0.24x |
| map_1m | 495.2 (441.2) | 591.2 (513.0) | 778.3 (681.2) | 0.84x ✅ win | 0.64x |
| object_deep_clone | 75.9 (57.9) | 40.3 (30.4) | 213.6 (125.3) | 1.88x ⚠ borderline | 0.36x |
| promise_all_chains | 129.9 (91.3) | 76.8 (58.7) | 212.6 (161.8) | 1.69x ⚠ borderline | 0.61x |
| regex_replace | 60.1 (44.3) | 156.7 (63.3) | 854.0 (494.4) | 0.38x ✅ win | 0.07x |
| string_concat_csv | 111.0 (54.2) | 84.0 (49.7) | 297.7 (180.7) | 1.32x ✓ ok | 0.37x |
| string_split_map_join | 92.3 (74.2) | 156.9 (133.3) | 323.9 (261.1) | 0.59x ✅ win | 0.28x |
| string_template_interp | 149.6 (80.4) | 134.4 (64.8) | 360.4 (289.3) | 1.11x ✓ ok | 0.42x |

## Summary

- Perry-faster-than-Bun kernels: **6**
- Borderline (1.5–2× slower): **2**
- Slow (≥ 2× slower): **0**

**Priority backlog:** the ✗ slow rows are the gaps. Each one is a focused workstream.
16 changes: 16 additions & 0 deletions benchmarks/app-patterns/results/matrix-20260521-230243.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# App-pattern bench matrix — 2026-05-21 23:02:43

Each cell is mean ms (min ms). Lower is better.
Final column is **perry/bun ratio** — > 2× is a real gap, < 1.5× is fine.

| Kernel | Perry | Bun | Node | perry/bun | perry/node |
|---|---:|---:|---:|---:|---:|
| object_deep_clone | 51.8 (41.2) | 41.0 (24.5) | 100.2 (87.4) | 1.26x ✓ ok | 0.52x |

## Summary

- Perry-faster-than-Bun kernels: **0**
- Borderline (1.5–2× slower): **0**
- Slow (≥ 2× slower): **0**

**Priority backlog:** the ✗ slow rows are the gaps. Each one is a focused workstream.
3 changes: 3 additions & 0 deletions crates/perry-codegen/src/codegen/closure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,8 @@ pub(super) fn compile_closure(
module_globals,
classes,
);
let non_escaping_new_used_fields =
crate::collectors::collect_non_escaping_new_used_fields(body, &non_escaping_news);
let non_escaping_arrays =
crate::collectors::collect_non_escaping_arrays(body, &closure_boxed_vars, module_globals);
let non_escaping_object_literals = crate::collectors::collect_non_escaping_object_literals(
Expand Down Expand Up @@ -306,6 +308,7 @@ pub(super) fn compile_closure(
scalar_replaced_arrays: std::collections::HashMap::new(),
scalar_ctor_target: Vec::new(),
non_escaping_news,
non_escaping_new_used_fields,
non_escaping_arrays,
non_escaping_object_literals,
flat_const_arrays: &cross_module.flat_const_arrays,
Expand Down
12 changes: 12 additions & 0 deletions crates/perry-codegen/src/codegen/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,11 @@ pub(super) fn compile_module_entry(
module_globals,
classes,
);
let main_non_escaping_new_used_fields =
crate::collectors::collect_non_escaping_new_used_fields(
&hir.init,
&main_non_escaping_news,
);
let main_non_escaping_arrays = crate::collectors::collect_non_escaping_arrays(
&hir.init,
&main_boxed_vars,
Expand Down Expand Up @@ -335,6 +340,7 @@ pub(super) fn compile_module_entry(
scalar_replaced_arrays: std::collections::HashMap::new(),
scalar_ctor_target: Vec::new(),
non_escaping_news: main_non_escaping_news,
non_escaping_new_used_fields: main_non_escaping_new_used_fields,
non_escaping_arrays: main_non_escaping_arrays,
non_escaping_object_literals: main_non_escaping_object_literals,
flat_const_arrays: &cross_module.flat_const_arrays,
Expand Down Expand Up @@ -637,6 +643,11 @@ pub(super) fn compile_module_entry(
module_globals,
classes,
);
let init_non_escaping_new_used_fields =
crate::collectors::collect_non_escaping_new_used_fields(
&hir.init,
&init_non_escaping_news,
);
let init_non_escaping_arrays = crate::collectors::collect_non_escaping_arrays(
&hir.init,
&init_boxed_vars,
Expand Down Expand Up @@ -723,6 +734,7 @@ pub(super) fn compile_module_entry(
scalar_replaced_arrays: std::collections::HashMap::new(),
scalar_ctor_target: Vec::new(),
non_escaping_news: init_non_escaping_news,
non_escaping_new_used_fields: init_non_escaping_new_used_fields,
non_escaping_arrays: init_non_escaping_arrays,
non_escaping_object_literals: init_non_escaping_object_literals,
flat_const_arrays: &cross_module.flat_const_arrays,
Expand Down
3 changes: 3 additions & 0 deletions crates/perry-codegen/src/codegen/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,8 @@ pub(super) fn compile_function(
// Pre-walk: which `let x = new Class(...)` locals never escape?
let non_escaping_news =
crate::collectors::collect_non_escaping_news(&f.body, &boxed_vars, module_globals, classes);
let non_escaping_new_used_fields =
crate::collectors::collect_non_escaping_new_used_fields(&f.body, &non_escaping_news);
let non_escaping_arrays =
crate::collectors::collect_non_escaping_arrays(&f.body, &boxed_vars, module_globals);
let non_escaping_object_literals = crate::collectors::collect_non_escaping_object_literals(
Expand Down Expand Up @@ -235,6 +237,7 @@ pub(super) fn compile_function(
scalar_replaced_arrays: std::collections::HashMap::new(),
scalar_ctor_target: Vec::new(),
non_escaping_news,
non_escaping_new_used_fields,
non_escaping_arrays,
non_escaping_object_literals,
flat_const_arrays: &cross_module.flat_const_arrays,
Expand Down
6 changes: 6 additions & 0 deletions crates/perry-codegen/src/codegen/method.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ pub(super) fn compile_method(
module_globals,
classes,
);
let non_escaping_new_used_fields =
crate::collectors::collect_non_escaping_new_used_fields(&method.body, &non_escaping_news);
let non_escaping_arrays = crate::collectors::collect_non_escaping_arrays(
&method.body,
&method_boxed_vars,
Expand Down Expand Up @@ -199,6 +201,7 @@ pub(super) fn compile_method(
scalar_replaced_arrays: std::collections::HashMap::new(),
scalar_ctor_target: Vec::new(),
non_escaping_news,
non_escaping_new_used_fields,
non_escaping_arrays,
non_escaping_object_literals,
flat_const_arrays: &cross_module.flat_const_arrays,
Expand Down Expand Up @@ -482,6 +485,8 @@ pub(super) fn compile_static_method(
module_globals,
classes,
);
let non_escaping_new_used_fields =
crate::collectors::collect_non_escaping_new_used_fields(&f.body, &non_escaping_news);
let non_escaping_arrays =
crate::collectors::collect_non_escaping_arrays(&f.body, &static_boxed_vars, module_globals);
let non_escaping_object_literals = crate::collectors::collect_non_escaping_object_literals(
Expand Down Expand Up @@ -569,6 +574,7 @@ pub(super) fn compile_static_method(
scalar_replaced_arrays: std::collections::HashMap::new(),
scalar_ctor_target: Vec::new(),
non_escaping_news,
non_escaping_new_used_fields,
non_escaping_arrays,
non_escaping_object_literals,
flat_const_arrays: &cross_module.flat_const_arrays,
Expand Down
50 changes: 50 additions & 0 deletions crates/perry-codegen/src/collectors/class_accessors.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
//! Getter / setter dispatch detection used by escape analysis.
//!
//! Split out of `escape_news.rs` in v0.5.1021 to satisfy the file-size CI
//! gate. No behavior change — these functions remain `pub` and are re-
//! exported from `collectors/mod.rs`.

/// Is `property` a getter on `class_name` (walking its inheritance chain)?
/// Used by escape analysis: a `LocalGet(candidate).gettableProp` access is
/// a real getter dispatch that needs `this` as a heap pointer, so the
/// candidate must escape.
pub fn is_class_getter(
classes: &std::collections::HashMap<String, &perry_hir::Class>,
class_name: &str,
property: &str,
) -> bool {
let mut cur = Some(class_name.to_string());
while let Some(name) = cur {
if let Some(class) = classes.get(&name) {
if class.getters.iter().any(|(n, _)| n == property) {
return true;
}
cur = class.extends_name.clone();
} else {
return false;
}
}
false
}

/// Mirror of `is_class_getter` for setters — used on the PropertySet/
/// PropertyUpdate paths where a setter dispatch (vs. a plain field write)
/// likewise needs a real `this` pointer.
pub fn is_class_setter(
classes: &std::collections::HashMap<String, &perry_hir::Class>,
class_name: &str,
property: &str,
) -> bool {
let mut cur = Some(class_name.to_string());
while let Some(name) = cur {
if let Some(class) = classes.get(&name) {
if class.setters.iter().any(|(n, _)| n == property) {
return true;
}
cur = class.extends_name.clone();
} else {
return false;
}
}
false
}
Loading