mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
test: 新增内存占用基准用例与留存内存采集
bench 每轮采集在 workload 后 promote 根持有/会话引用的 epoch 对象并强制 一次 full GC,新增 peak_bytes / retained_bytes / retained_objects 三口径 (原 session_* 字段为执行期累计分配,不反映 workload 后留存);dispatch 循环增加 session 堆高水位采样,VM 暴露 session_bytes_peak 与 collect_session_gc。新增 8 个 mem_* 压力用例(对象图/字符串/闭包链/数组 增删/跨调用保留/Map/Set 保留/字符串拼接),基线扩至 76 条目。
This commit is contained in:
+92
-68
@@ -1,68 +1,92 @@
|
|||||||
Test Name Wall(ms) Comp(ms) Exec(ms) Instrs GC Ct IC Rate Sess Objs Sess B
|
Test Name Wall(ms) Comp(ms) Exec(ms) Instrs GC Ct IC Rate Sess Objs Sess B Peak B Ret B
|
||||||
------------------------------------------------------------------------------------------------------------------------
|
------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
arith_ops 23.65 0.000 23.652 3000019 0 0.0% 0 0
|
arith_ops 47.27 0.000 47.271 3000026 0 0.0% 0 0 0 0
|
||||||
array_filter 232.56 0.000 232.558 41231 0 0.0% 2001 380120
|
array_filter 405.68 0.001 405.674 41244 0 51.2% 6001 1228144 1228144 2176
|
||||||
array_for_each 248.79 0.000 248.791 25231 0 0.0% 1 140120
|
array_for_each 374.59 0.001 374.588 25244 0 62.7% 4001 140144 140144 1088
|
||||||
array_index_get 8.60 0.000 8.602 1012031 0 0.0% 1 120
|
array_index_get 9.84 0.000 9.835 1012042 0 70.6% 1 144 8288 8288
|
||||||
array_index_set 12.24 0.000 12.243 1012029 0 0.0% 1 120
|
array_index_set 9.38 0.000 9.376 1012038 0 74.3% 1 144 8288 8288
|
||||||
array_join 165.75 0.000 165.750 3040019 0 0.0% 20000 4180000
|
array_join 113.52 0.001 113.520 3040032 0 98.4% 20000 4660000 4660000 419
|
||||||
array_literal 51.09 0.000 51.094 1150019 0 0.0% 50000 6000000
|
array_literal 84.01 0.000 84.007 1150028 0 87.4% 50000 8400000 8400000 336
|
||||||
array_map 224.96 0.000 224.960 41231 0 0.0% 2001 380120
|
array_map 386.85 0.001 386.846 41244 0 81.4% 6001 2028144 2028144 2976
|
||||||
array_push_pop 321.18 0.000 321.182 7450019 0 0.0% 50000 6000000
|
array_push_pop 215.52 0.001 215.517 7450032 0 91.3% 50000 7200000 7200000 360
|
||||||
array_reduce 254.39 0.000 254.386 43231 0 0.0% 1 140120
|
array_reduce 417.76 0.001 417.759 43244 0 93.7% 4001 140144 140144 1584
|
||||||
array_slice 73.67 0.000 73.674 58231 0 0.0% 3001 20928120
|
array_slice 85.12 0.001 85.122 58244 0 93.8% 3001 30672144 30672144 7824
|
||||||
array_sort 71.47 0.000 71.468 13931 0 0.0% 501 3504120
|
array_sort 108.11 0.000 108.110 13944 0 93.8% 1501 5128144 5128144 7824
|
||||||
branch_if 10.45 0.000 10.445 1866686 0 0.0% 0 0
|
branch_if 22.25 0.000 22.249 1866693 0 93.8% 0 0 0 0
|
||||||
branch_switch 10.60 0.000 10.604 1875019 0 0.0% 0 0
|
branch_switch 21.67 0.000 21.674 1875026 0 93.8% 0 0 0 0
|
||||||
call_args 36.06 0.000 36.064 760023 0 0.0% 2 275
|
call_args 51.47 0.001 51.465 720030 0 93.8% 2 35 499 499
|
||||||
call_closure 24.28 0.000 24.282 340035 0 0.0% 4 549
|
call_closure 24.62 0.000 24.622 340046 0 93.8% 4 69 997 997
|
||||||
call_method 29.61 0.000 29.605 420027 0 100.0% 3 395
|
call_method 32.01 0.000 32.014 420036 0 92.5% 3 195 819 819
|
||||||
call_new_proto 71.14 0.000 71.140 660029 0 37.2% 100002 12000310
|
call_new_proto 83.91 0.000 83.910 660038 0 89.4% 20004 3040070 3040070 1334
|
||||||
call_recursion 348.35 0.000 348.354 5525823 0 25.0% 2 275
|
call_recursion 522.22 0.001 522.221 5525830 0 88.0% 2 35 499 499
|
||||||
call_this 46.73 0.000 46.729 620029 0 37.9% 5 680
|
call_this 63.87 0.001 63.866 620038 0 86.1% 5 248 1344 1344
|
||||||
cmp_ops 29.79 0.000 29.792 3600019 0 42.9% 0 0
|
cmp_ops 70.09 0.000 70.095 3600028 0 85.0% 0 0 0 0
|
||||||
code_forge 0.01 0.000 0.005 3600019 0 42.9% 0 329
|
code_forge 804.26 0.000 804.263 85020 0 85.0% 10000 291670 291670 0
|
||||||
coercion_boolean 15.92 0.000 15.923 3050019 0 42.9% 0 0
|
coercion_boolean 36.78 0.000 36.776 3050026 0 85.0% 0 0 0 0
|
||||||
coercion_concat 41.84 0.000 41.837 1600019 0 42.9% 0 3888890
|
coercion_concat 55.45 0.000 55.452 1600028 0 85.0% 0 3888890 3888890 39
|
||||||
coercion_nullish 66.91 0.000 66.911 3650025 0 59.9% 1 120
|
coercion_nullish 109.39 0.000 109.392 3650036 0 81.3% 1 152 304 304
|
||||||
coercion_tonumber 14.94 0.000 14.935 2100019 0 66.7% 0 0
|
coercion_tonumber 29.99 0.000 29.989 1800026 0 79.3% 0 0 0 0
|
||||||
coercion_toprimitive 88.44 0.000 88.442 360019 0 66.7% 100000 13580000
|
coercion_toprimitive 111.99 0.001 111.990 360028 0 79.3% 100000 4780000 4780000 1327
|
||||||
coercion_tostring 123.01 0.000 123.014 4200019 0 66.7% 0 34200000
|
coercion_tostring 166.51 0.000 166.509 3900028 0 79.3% 0 22200000 22200000 186
|
||||||
gc_array 137.04 0.000 137.036 3110019 0 66.7% 5000 600000
|
gc_array 77.05 0.000 77.048 3110030 0 81.4% 5000 720000 720000 688
|
||||||
gc_object 25.42 0.000 25.418 1200019 0 73.4% 50000 6000000
|
gc_object 61.56 0.000 61.563 1200028 0 83.2% 50000 8000000 8000000 160
|
||||||
gc_string 6.89 0.000 6.885 320019 0 76.5% 0 788890
|
gc_string 10.66 0.000 10.660 320028 0 83.6% 0 788890 788890 40
|
||||||
ic_cache 14.22 0.000 14.216 412035 0 75.3% 1 120
|
gc_string_long 595.93 0.000 595.930 16000028 1 83.6% 0 7334531 33554441 41
|
||||||
ic_polymorphic 74.38 0.000 74.383 3433378 0 60.9% 100003 12000360
|
ic_cache 12.00 0.000 12.002 412044 0 83.6% 1 144 1642960 1642960
|
||||||
loop_do_while 6.60 0.000 6.604 1400015 0 55.7% 0 0
|
ic_polymorphic 93.71 0.000 93.711 3333393 0 85.3% 100003 16800504 16800504 1176
|
||||||
loop_for 37.24 0.000 37.240 8000019 0 55.7% 0 0
|
loop_do_while 12.68 0.000 12.678 1400022 0 86.2% 0 0 0 0
|
||||||
loop_for_in 146.11 0.000 146.112 4561231 0 55.7% 1 3610
|
loop_for 69.38 0.000 69.379 8000026 0 86.2% 0 0 0 0
|
||||||
loop_for_of 345.58 0.000 345.577 2734231 0 55.7% 1 108120
|
loop_for_in 183.54 0.000 183.538 4561244 0 86.2% 1 3610 3610 1064
|
||||||
loop_for_of_string 222.06 0.000 222.054 2266931 0 55.7% 0 58150
|
loop_for_of 412.69 0.000 412.684 2734244 0 86.2% 1 108144 108144 1088
|
||||||
loop_index 231.48 0.000 231.478 3649231 0 55.7% 1 120
|
loop_for_of_string 256.13 0.000 256.129 2266944 0 86.2% 0 58150 58150 132
|
||||||
loop_while 33.83 0.000 33.831 8000019 0 55.7% 0 0
|
loop_index 314.94 0.000 314.940 3649244 0 78.4% 1 144 1088 1088
|
||||||
loop_while_index 217.07 0.000 217.073 3649231 0 55.7% 1 120
|
loop_while 69.48 0.000 69.484 8000026 0 74.3% 0 0 0 0
|
||||||
map_ops 298.46 0.000 298.455 9850019 0 55.7% 0 0
|
loop_while_index 314.96 0.000 314.961 3649244 0 68.5% 1 144 1088 1088
|
||||||
math_ops 101.06 0.000 101.063 4800019 0 55.7% 0 0
|
map_ops 232.57 0.001 232.570 9850030 0 69.4% 50000 9600000 9600000 544
|
||||||
object_keys 204.96 0.000 204.959 2680019 0 55.7% 10000 15000000
|
math_ops 130.99 0.000 130.987 4800026 0 71.6% 0 0 0 0
|
||||||
prop_access 11.71 0.000 11.715 2200031 0 67.6% 1 120
|
mem_array_grow 200.91 0.000 200.915 2800026 0 71.6% 1 144 8288 8288
|
||||||
prop_bracket 12.05 0.000 12.047 1400029 0 74.0% 1 120
|
mem_closure_chain 86.90 0.000 86.901 480040 0 71.6% 40000 1278890 8799386 8799386
|
||||||
prop_delete 22.46 0.000 22.462 320029 0 74.4% 1 120
|
mem_cross_call_retain 159.32 0.001 159.322 1380034 0 70.4% 4 2670485 7951253 7951253
|
||||||
prop_dynamic 3.22 0.000 3.221 700023 0 75.3% 1 120
|
mem_map_retain 42.20 0.001 42.194 500023 0 69.9% 1 1977972 4609319 4609319
|
||||||
prop_nested 45.30 0.000 45.296 1200019 0 77.7% 150000 18000000
|
mem_object_graph 111.83 0.001 111.828 884726 0 70.0% 32769 5504888 11010200 11010200
|
||||||
prop_set 28.20 0.000 28.204 2400027 0 78.0% 1 120
|
mem_set_retain 32.60 0.001 32.603 360023 0 70.1% 1 1089082 4449274 4449274
|
||||||
property 87.66 0.000 87.663 72031 0 77.8% 1 231900
|
mem_string_accum 69.16 0.000 69.162 760022 0 70.2% 1 2744617 3064761 3064761
|
||||||
set_ops 283.31 0.000 283.314 7150019 0 77.8% 0 0
|
mem_string_rope 0.55 0.000 0.547 10028 1 70.2% 0 62108000 62108000 62108000
|
||||||
string_build 168.54 0.000 168.541 8440019 0 77.8% 0 73900000
|
member_compound 488.16 0.000 488.157 3200082 0 71.8% 100006 16000968 16000968 2104
|
||||||
string_case 44.80 0.000 44.799 1200023 0 77.8% 0 7600000
|
object_keys 313.98 0.001 313.981 2680030 0 72.6% 10000 15000000 15000000 424
|
||||||
string_char 75.01 0.000 75.014 2800019 0 77.8% 0 0
|
prop_access 21.34 0.000 21.341 2000040 0 74.3% 1 176 352 352
|
||||||
string_concat 36.82 0.000 36.817 1800019 0 77.8% 0 9000000
|
prop_bracket 29.50 0.000 29.496 1400040 0 75.7% 1 168 336 336
|
||||||
string_indexof 18.26 0.000 18.258 700023 0 77.8% 0 0
|
prop_delete 22.46 0.000 22.465 320038 0 75.8% 1 168 336 336
|
||||||
string_replace 13.25 0.000 13.248 460023 0 77.8% 0 1320000
|
prop_dynamic 6.19 0.000 6.190 700032 0 76.0% 1 120 272 272
|
||||||
string_replace_fn 122.85 0.000 122.849 50631 0 77.8% 0 4051150
|
prop_nested 261.03 0.001 261.030 1200028 0 76.8% 150000 22800000 22800000 912
|
||||||
string_split 74.43 0.000 74.426 400023 0 77.8% 20000 19560000
|
prop_set 31.92 0.000 31.922 2400036 0 78.3% 1 160 320 320
|
||||||
string_substring 38.86 0.000 38.860 1150023 0 77.8% 0 2150000
|
property 154.55 0.001 154.547 72040 0 79.0% 1 231900 231900 24264
|
||||||
string_trim 12.50 0.000 12.502 300023 0 77.8% 0 1560000
|
set_ops 372.45 0.001 372.447 7150030 0 79.8% 50000 9600000 9600000 464
|
||||||
try_catch_ok 2.12 0.000 2.116 550019 0 77.8% 0 0
|
string_build 458.30 0.000 458.295 8440030 2 80.3% 0 6791158 33554485 122
|
||||||
try_catch_throw 21.15 0.000 21.148 400019 0 77.9% 0 720000
|
string_case 69.83 0.000 69.830 1200032 0 80.3% 0 7600000 7600000 0
|
||||||
typeof_ops 88.37 0.000 88.370 2600019 0 78.0% 0 15200000
|
string_char 99.59 0.000 99.591 2800028 0 80.3% 0 0 0 0
|
||||||
unary_ops 11.52 0.000 11.517 2000019 0 78.0% 0 0
|
string_concat 70.02 0.000 70.020 1600034 0 80.3% 0 1900000 1900000 38
|
||||||
|
string_indexof 24.26 0.000 24.255 700032 0 80.3% 0 0 0 0
|
||||||
|
string_replace 19.15 0.000 19.149 460034 0 80.3% 0 1320000 1320000 66
|
||||||
|
string_replace_fn 238.27 0.001 238.267 50592 0 80.3% 4000 4049360 4049360 7177
|
||||||
|
string_split 133.50 0.000 133.495 400034 0 80.1% 20000 24200000 24200000 1562
|
||||||
|
string_substring 43.37 0.000 43.367 1150034 0 80.0% 0 2150000 2150000 43
|
||||||
|
string_trim 11.91 0.000 11.906 300032 0 80.0% 0 1560000 1560000 0
|
||||||
|
try_catch_ok 4.35 0.000 4.348 550026 0 80.0% 0 0 0 0
|
||||||
|
try_catch_throw 26.10 0.000 26.101 400026 0 80.0% 0 720000 720000 0
|
||||||
|
typeof_ops 51.55 0.000 51.544 2600026 0 80.0% 0 0 0 0
|
||||||
|
unary_ops 25.55 0.000 25.555 2000026 0 80.0% 0 0 0 0
|
||||||
|
|
||||||
|
内存基准用例(mem_*):
|
||||||
|
|
||||||
|
mem_object_graph 大型对象图留存:15 层满二叉树 32767 节点整体驻留 session 堆
|
||||||
|
mem_string_accum 字符串累积留存:40000 个互不相同的字符串驻留数组(interner 压力)
|
||||||
|
mem_closure_chain 闭包/upvalue 长链:10000 层闭包各自捕获前层闭包与 payload 驻留
|
||||||
|
mem_array_grow 数组增缩容量:扩到 200000 元素后截回 1000,观测元素向量容量是否回落(账目按 Vec capacity 计)
|
||||||
|
mem_cross_call_retain 跨调用长存活数据:全局 store 经 30000 次调用累积 30000 条记录
|
||||||
|
mem_map_retain 原生盒边持有:Map 持 20000 组字符串键 + 对象值
|
||||||
|
mem_set_retain 原生盒边持有:Set 持 20000 个对象(各带 payload 字符串)
|
||||||
|
mem_string_rope 拼接 rope 链留存:重复二元拼接形成深 Cons 链(账目按逻辑长度计,数值高于实际足迹)
|
||||||
|
口径:Peak B/Ret B 为 session 堆账目(对象头 + 堆数据 + 存活串/BigInt);epoch arena
|
||||||
|
的临时驻留无字节账目(既有口径,数量见 JSON 的 epoch_objects)。Ret B 为 workload 后
|
||||||
|
晋升 + 完整 GC 的真存活集。
|
||||||
|
|||||||
+1016
-608
File diff suppressed because it is too large
Load Diff
@@ -79,7 +79,7 @@ pub fn compare_baseline(current: &[MetricCollection]) -> Vec<Regression> {
|
|||||||
let tolerance = match name {
|
let tolerance = match name {
|
||||||
"wall_time_us" => 0.10,
|
"wall_time_us" => 0.10,
|
||||||
"gc_trigger_count" | "gc_bytes_freed" | "gc_objects_scanned" | "gc_collection_us" => 0.50,
|
"gc_trigger_count" | "gc_bytes_freed" | "gc_objects_scanned" | "gc_collection_us" => 0.50,
|
||||||
"session_bytes" => 0.20,
|
"session_bytes" | "peak_bytes" | "retained_bytes" | "retained_objects" => 0.20,
|
||||||
_ => 0.20,
|
_ => 0.20,
|
||||||
};
|
};
|
||||||
let ratio = cur_val / base_val;
|
let ratio = cur_val / base_val;
|
||||||
|
|||||||
@@ -91,25 +91,53 @@ pub fn run_js_stress_bench(config: &BenchConfig, kernel: &Arc<KernelCore>, pool:
|
|||||||
let _result = vm.run(&module);
|
let _result = vm.run(&module);
|
||||||
let exec_time = exec_start.elapsed();
|
let exec_time = exec_start.elapsed();
|
||||||
|
|
||||||
|
// 执行期指标须在强制 GC 前读取:GC 会搬移/清扫 session 对象,
|
||||||
|
// 其后读到的计数是存活集而非本执行期的累计量
|
||||||
let gc_stats = vm.session_gc_stats();
|
let gc_stats = vm.session_gc_stats();
|
||||||
|
let gc_trigger_count = gc_stats.total_collections;
|
||||||
|
let gc_bytes_freed = gc_stats.last_collection_bytes_freed;
|
||||||
|
let gc_objects_scanned = gc_stats.last_collection_objects_scanned;
|
||||||
|
let gc_collection_us = gc_stats.last_collection_duration_us;
|
||||||
|
let session_objects = vm.session_object_count().saturating_sub(pre_session);
|
||||||
|
let session_bytes = vm.session_bytes_allocated();
|
||||||
|
let epoch_objects = vm.epoch_object_count().saturating_sub(pre_epoch);
|
||||||
|
let instruction_count = vm.instruction_count();
|
||||||
|
let ic_hit_rate = vm.ic_hit_rate();
|
||||||
|
let ic_hits = vm.ic_hit_count();
|
||||||
|
let ic_misses = vm.ic_miss_count();
|
||||||
|
|
||||||
|
// 留存口径:先让存活集对 session 完全可见——根直接持有的 epoch 对象
|
||||||
|
// (顶层 var 等)晋升进 session,session 对象持有的 epoch 子引用
|
||||||
|
// (闭包捕获、原生盒直插)同样晋升(均与 epoch 边界同机制);
|
||||||
|
// 再强制完整 GC 清掉不可达垃圾,清扫后账目即 workload 留存堆
|
||||||
|
vm.promote_rooted_epoch_objects();
|
||||||
|
vm.promote_session_epoch_refs();
|
||||||
|
vm.collect_session_gc();
|
||||||
|
let retained_bytes = vm.session_bytes_allocated();
|
||||||
|
let retained_objects = vm.session_object_count();
|
||||||
|
// 峰值高水位取执行期采样与 GC 后存活字节的较大者(存活集亦属高水位)
|
||||||
|
let peak_bytes = vm.session_bytes_peak().max(retained_bytes);
|
||||||
|
|
||||||
metrics.push(MetricCollection {
|
metrics.push(MetricCollection {
|
||||||
test_name: test_name.clone(),
|
test_name: test_name.clone(),
|
||||||
wall_time_us: (compile_time + exec_time).as_micros() as u64,
|
wall_time_us: (compile_time + exec_time).as_micros() as u64,
|
||||||
session_objects: vm.session_object_count().saturating_sub(pre_session),
|
session_objects,
|
||||||
session_bytes: vm.session_bytes_allocated(),
|
session_bytes,
|
||||||
epoch_objects: vm.epoch_object_count().saturating_sub(pre_epoch),
|
epoch_objects,
|
||||||
epoch_bytes: 0,
|
epoch_bytes: 0,
|
||||||
gc_trigger_count: gc_stats.total_collections,
|
gc_trigger_count,
|
||||||
gc_bytes_freed: gc_stats.last_collection_bytes_freed,
|
gc_bytes_freed,
|
||||||
gc_objects_scanned: gc_stats.last_collection_objects_scanned,
|
gc_objects_scanned,
|
||||||
gc_collection_us: gc_stats.last_collection_duration_us,
|
gc_collection_us,
|
||||||
instruction_count: vm.instruction_count(),
|
instruction_count,
|
||||||
compile_time_us: compile_time.as_micros() as u64,
|
compile_time_us: compile_time.as_micros() as u64,
|
||||||
exec_time_us: exec_time.as_micros() as u64,
|
exec_time_us: exec_time.as_micros() as u64,
|
||||||
ic_hit_rate: vm.ic_hit_rate(),
|
ic_hit_rate,
|
||||||
ic_hits: vm.ic_hit_count(),
|
ic_hits,
|
||||||
ic_misses: vm.ic_miss_count(),
|
ic_misses,
|
||||||
|
peak_bytes: peak_bytes as u64,
|
||||||
|
retained_bytes: retained_bytes as u64,
|
||||||
|
retained_objects: retained_objects as u64,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,6 +198,9 @@ fn average_metrics(metrics: &[MetricCollection]) -> MetricCollection {
|
|||||||
ic_hit_rate: 0.0,
|
ic_hit_rate: 0.0,
|
||||||
ic_hits: 0,
|
ic_hits: 0,
|
||||||
ic_misses: 0,
|
ic_misses: 0,
|
||||||
|
peak_bytes: 0,
|
||||||
|
retained_bytes: 0,
|
||||||
|
retained_objects: 0,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
MetricCollection {
|
MetricCollection {
|
||||||
@@ -189,5 +220,8 @@ fn average_metrics(metrics: &[MetricCollection]) -> MetricCollection {
|
|||||||
ic_hit_rate: metrics.iter().map(|m| m.ic_hit_rate).sum::<f64>() / n,
|
ic_hit_rate: metrics.iter().map(|m| m.ic_hit_rate).sum::<f64>() / n,
|
||||||
ic_hits: (metrics.iter().map(|m| m.ic_hits as f64).sum::<f64>() / n) as u64,
|
ic_hits: (metrics.iter().map(|m| m.ic_hits as f64).sum::<f64>() / n) as u64,
|
||||||
ic_misses: (metrics.iter().map(|m| m.ic_misses as f64).sum::<f64>() / n) as u64,
|
ic_misses: (metrics.iter().map(|m| m.ic_misses as f64).sum::<f64>() / n) as u64,
|
||||||
|
peak_bytes: (metrics.iter().map(|m| m.peak_bytes as f64).sum::<f64>() / n) as u64,
|
||||||
|
retained_bytes: (metrics.iter().map(|m| m.retained_bytes as f64).sum::<f64>() / n) as u64,
|
||||||
|
retained_objects: (metrics.iter().map(|m| m.retained_objects as f64).sum::<f64>() / n) as u64,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -138,6 +138,9 @@ pub fn run_leak_detect(config: &BenchConfig, kernel: &Arc<KernelCore>, pool: &Ar
|
|||||||
ic_hit_rate: vm.ic_hit_rate(),
|
ic_hit_rate: vm.ic_hit_rate(),
|
||||||
ic_hits: vm.ic_hit_count(),
|
ic_hits: vm.ic_hit_count(),
|
||||||
ic_misses: vm.ic_miss_count(),
|
ic_misses: vm.ic_miss_count(),
|
||||||
|
peak_bytes: vm.session_bytes_peak() as u64,
|
||||||
|
retained_bytes: vm.session_bytes_allocated() as u64,
|
||||||
|
retained_objects: vm.session_object_count() as u64,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,15 @@ pub struct MetricCollection {
|
|||||||
pub ic_hit_rate: f64,
|
pub ic_hit_rate: f64,
|
||||||
pub ic_hits: u64,
|
pub ic_hits: u64,
|
||||||
pub ic_misses: u64,
|
pub ic_misses: u64,
|
||||||
|
/// 执行期 session 堆账目的峰值高水位(字节)。旧基线文件无此字段,反序列化补 0。
|
||||||
|
#[serde(default)]
|
||||||
|
pub peak_bytes: u64,
|
||||||
|
/// workload 结束并强制完整 GC 后的 session 存活堆字节(留存内存)。
|
||||||
|
#[serde(default)]
|
||||||
|
pub retained_bytes: u64,
|
||||||
|
/// workload 结束并强制完整 GC 后的 session 存活对象数。
|
||||||
|
#[serde(default)]
|
||||||
|
pub retained_objects: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MetricCollection {
|
impl MetricCollection {
|
||||||
@@ -40,6 +49,9 @@ impl MetricCollection {
|
|||||||
("ic_hit_rate", self.ic_hit_rate),
|
("ic_hit_rate", self.ic_hit_rate),
|
||||||
("ic_hits", self.ic_hits as f64),
|
("ic_hits", self.ic_hits as f64),
|
||||||
("ic_misses", self.ic_misses as f64),
|
("ic_misses", self.ic_misses as f64),
|
||||||
|
("peak_bytes", self.peak_bytes as f64),
|
||||||
|
("retained_bytes", self.retained_bytes as f64),
|
||||||
|
("retained_objects", self.retained_objects as f64),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,19 +5,47 @@ pub fn format_json(results: &[MetricCollection]) -> String {
|
|||||||
serde_json::to_string_pretty(results).unwrap_or_else(|e| format!("{{\"error\": \"{}\"}}", e))
|
serde_json::to_string_pretty(results).unwrap_or_else(|e| format!("{{\"error\": \"{}\"}}", e))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 内存基准用例(`mem_*` 前缀)的留存形态说明,随表格尾部输出。
|
||||||
|
const MEMORY_BENCH_NOTES: &str = "\n内存基准用例(mem_*):\n
|
||||||
|
mem_object_graph 大型对象图留存:15 层满二叉树 32767 节点整体驻留 session 堆
|
||||||
|
mem_string_accum 字符串累积留存:40000 个互不相同的字符串驻留数组(interner 压力)
|
||||||
|
mem_closure_chain 闭包/upvalue 长链:10000 层闭包各自捕获前层闭包与 payload 驻留
|
||||||
|
mem_array_grow 数组增缩容量:扩到 200000 元素后截回 1000,观测元素向量容量是否回落(账目按 Vec capacity 计)
|
||||||
|
mem_cross_call_retain 跨调用长存活数据:全局 store 经 30000 次调用累积 30000 条记录
|
||||||
|
mem_map_retain 原生盒边持有:Map 持 20000 组字符串键 + 对象值
|
||||||
|
mem_set_retain 原生盒边持有:Set 持 20000 个对象(各带 payload 字符串)
|
||||||
|
mem_string_rope 拼接 rope 链留存:重复二元拼接形成深 Cons 链(账目按逻辑长度计,数值高于实际足迹)
|
||||||
|
口径:Peak B/Ret B 为 session 堆账目(对象头 + 堆数据 + 存活串/BigInt);epoch arena
|
||||||
|
的临时驻留无字节账目(既有口径,数量见 JSON 的 epoch_objects)。Ret B 为 workload 后
|
||||||
|
晋升 + 完整 GC 的真存活集。
|
||||||
|
";
|
||||||
|
|
||||||
/// 把结果格式化为对齐的文本表格(供终端打印)。
|
/// 把结果格式化为对齐的文本表格(供终端打印)。
|
||||||
|
///
|
||||||
|
/// `Sess B`/`Peak B`/`Ret B` 为 session 堆账目(字节):执行期累计 / 峰值高水位 /
|
||||||
|
/// workload 后强制 GC 的存活留存。
|
||||||
pub fn format_text_table(results: &[MetricCollection]) -> String {
|
pub fn format_text_table(results: &[MetricCollection]) -> String {
|
||||||
let mut out = String::new();
|
let mut out = String::new();
|
||||||
out.push_str(&format!(
|
out.push_str(&format!(
|
||||||
"{:<35} {:>10} {:>10} {:>10} {:>12} {:>8} {:>8} {:>10} {:>10}\n",
|
"{:<35} {:>10} {:>10} {:>10} {:>12} {:>8} {:>8} {:>10} {:>10} {:>10} {:>10}\n",
|
||||||
"Test Name", "Wall(ms)", "Comp(ms)", "Exec(ms)", "Instrs", "GC Ct", "IC Rate", "Sess Objs", "Sess B",
|
"Test Name",
|
||||||
|
"Wall(ms)",
|
||||||
|
"Comp(ms)",
|
||||||
|
"Exec(ms)",
|
||||||
|
"Instrs",
|
||||||
|
"GC Ct",
|
||||||
|
"IC Rate",
|
||||||
|
"Sess Objs",
|
||||||
|
"Sess B",
|
||||||
|
"Peak B",
|
||||||
|
"Ret B",
|
||||||
));
|
));
|
||||||
out.push_str(&"-".repeat(120));
|
out.push_str(&"-".repeat(150));
|
||||||
out.push('\n');
|
out.push('\n');
|
||||||
|
|
||||||
for m in results {
|
for m in results {
|
||||||
out.push_str(&format!(
|
out.push_str(&format!(
|
||||||
"{:<35} {:>10.2} {:>10.3} {:>10.3} {:>12} {:>8} {:>7.1}% {:>10} {:>10}",
|
"{:<35} {:>10.2} {:>10.3} {:>10.3} {:>12} {:>8} {:>7.1}% {:>10} {:>10} {:>10} {:>10}",
|
||||||
truncate(&m.test_name, 35),
|
truncate(&m.test_name, 35),
|
||||||
m.wall_time_us as f64 / 1000.0,
|
m.wall_time_us as f64 / 1000.0,
|
||||||
m.compile_time_us as f64 / 1000.0,
|
m.compile_time_us as f64 / 1000.0,
|
||||||
@@ -27,9 +55,12 @@ pub fn format_text_table(results: &[MetricCollection]) -> String {
|
|||||||
m.ic_hit_rate * 100.0,
|
m.ic_hit_rate * 100.0,
|
||||||
m.session_objects,
|
m.session_objects,
|
||||||
m.session_bytes,
|
m.session_bytes,
|
||||||
|
m.peak_bytes,
|
||||||
|
m.retained_bytes,
|
||||||
));
|
));
|
||||||
out.push('\n');
|
out.push('\n');
|
||||||
}
|
}
|
||||||
|
out.push_str(MEMORY_BENCH_NOTES);
|
||||||
out
|
out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ impl Vm {
|
|||||||
/// 函数目标豁免均直落 epoch 值);Map/Set 等原生盒按键值直插 epoch 值。
|
/// 函数目标豁免均直落 epoch 值);Map/Set 等原生盒按键值直插 epoch 值。
|
||||||
/// `rewrite_object_values` 覆盖元素/meta/属性/proto/captured_this/home_object/
|
/// `rewrite_object_values` 覆盖元素/meta/属性/proto/captured_this/home_object/
|
||||||
/// cell 值;克隆子树经 forwarding 去重,环与共享引用各克隆一次。
|
/// cell 值;克隆子树经 forwarding 去重,环与共享引用各克隆一次。
|
||||||
pub(crate) fn promote_session_epoch_refs(&mut self) {
|
pub fn promote_session_epoch_refs(&mut self) {
|
||||||
let objects = std::mem::take(&mut self.gc_state.session_object_ptrs);
|
let objects = std::mem::take(&mut self.gc_state.session_object_ptrs);
|
||||||
if objects.is_empty() {
|
if objects.is_empty() {
|
||||||
return;
|
return;
|
||||||
@@ -185,6 +185,40 @@ impl Vm {
|
|||||||
self.gc_state.session_object_ptrs.extend(objects);
|
self.gc_state.session_object_ptrs.extend(objects);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 把根直接持有的 epoch 对象(顶层 var 寄存器、挂起句柄等)晋升进 session,
|
||||||
|
/// 并把根引用改写到克隆体。
|
||||||
|
///
|
||||||
|
/// # 副作用
|
||||||
|
/// - 每个根 epoch 对象深克隆进 session(含 native 状态盒,子引用递归晋升),
|
||||||
|
/// 账目计入克隆字节;根引用按转发表重写。
|
||||||
|
///
|
||||||
|
/// # 注意事项
|
||||||
|
/// - 供 workload 后观测点(基准留存测量)使用:本方法之后存活集完全对
|
||||||
|
/// session 可见,后续完整 GC 的留存账目不再漏"仅驻留 epoch arena 的对象"。
|
||||||
|
/// - 与 `promote_session_epoch_refs` 互补:本方法处理根直接持有的 epoch 对象,
|
||||||
|
/// 后者处理 session 对象持有的 epoch 子引用(闭包捕获等绕过写屏障的来源)。
|
||||||
|
pub fn promote_rooted_epoch_objects(&mut self) {
|
||||||
|
let mut epoch_roots = Vec::new();
|
||||||
|
self.for_each_root(|value| {
|
||||||
|
if value.is_object() {
|
||||||
|
let ptr = value.as_js_object_ptr();
|
||||||
|
if !ptr.is_null() {
|
||||||
|
// SAFETY: 根上的对象值均为 VM 自有存活 JsObject。
|
||||||
|
if unsafe { &*ptr }.is_epoch() {
|
||||||
|
epoch_roots.push(ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let mut forwarding = std::mem::take(&mut self.gc_state.forwarding);
|
||||||
|
for ptr in epoch_roots {
|
||||||
|
self.promote_object_inner(ptr, &mut forwarding);
|
||||||
|
}
|
||||||
|
crate::session_gc::rewrite_vm_roots(self, &forwarding);
|
||||||
|
forwarding.clear();
|
||||||
|
self.gc_state.forwarding = forwarding;
|
||||||
|
}
|
||||||
|
|
||||||
/// 逃逸写屏障:写向全局/session 对象的对象值不指向 epoch,否则 epoch
|
/// 逃逸写屏障:写向全局/session 对象的对象值不指向 epoch,否则 epoch
|
||||||
/// 重置后悬垂。session 函数目标豁免晋升——克隆会令写入侧与字节码持有的
|
/// 重置后悬垂。session 函数目标豁免晋升——克隆会令写入侧与字节码持有的
|
||||||
/// 原始对象分裂(类构造器在原型上续建方法会落到非克隆体);函数持有的
|
/// 原始对象分裂(类构造器在原型上续建方法会落到非克隆体);函数持有的
|
||||||
|
|||||||
@@ -915,7 +915,7 @@ fn rewrite_forwarded_value(
|
|||||||
.unwrap_or(value)
|
.unwrap_or(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rewrite_vm_roots(vm: &mut Vm, forwarding: &HashMap<*mut JsObject, *mut JsObject, FxBuildHasher>) {
|
pub(crate) fn rewrite_vm_roots(vm: &mut Vm, forwarding: &HashMap<*mut JsObject, *mut JsObject, FxBuildHasher>) {
|
||||||
vm_debug!("[GC] rewrite_vm_roots: {} forwarded objects", forwarding.len());
|
vm_debug!("[GC] rewrite_vm_roots: {} forwarded objects", forwarding.len());
|
||||||
// 统一遍历:与 for_each_value 共用同一字段清单。
|
// 统一遍历:与 for_each_value 共用同一字段清单。
|
||||||
vm.rewrite_values(|value| rewrite_forwarded_value(value, forwarding));
|
vm.rewrite_values(|value| rewrite_forwarded_value(value, forwarding));
|
||||||
|
|||||||
@@ -969,6 +969,26 @@ impl Vm {
|
|||||||
self.gc_state.session_bytes_allocated
|
self.gc_state.session_bytes_allocated
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 执行期 session 堆账目的峰值高水位(顶层指令边界采样,全量重置清零)。
|
||||||
|
pub fn session_bytes_peak(&self) -> usize {
|
||||||
|
self.gc_state.session_bytes_peak
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 无条件执行一次完整 session GC(mark + 移动式 sweep + 串/BigInt 清扫)。
|
||||||
|
///
|
||||||
|
/// # 副作用
|
||||||
|
/// - 存活对象复制进新 session arena,全部根与原生盒按转发表重写;
|
||||||
|
/// `session_bytes_allocated` 重置为清扫后的存活字节。
|
||||||
|
///
|
||||||
|
/// # 注意事项
|
||||||
|
/// - 须在执行外的安全点调用(无在途 builtin 局部裸指针、dispatch 未重入);
|
||||||
|
/// 执行期触发仍走水位路径,本入口供事后观测(如基准测 workload 后留存堆)。
|
||||||
|
pub fn collect_session_gc(&mut self) {
|
||||||
|
let mut session_gc = std::mem::take(&mut self.gc_state.session_gc);
|
||||||
|
session_gc.collect(self);
|
||||||
|
self.gc_state.session_gc = session_gc;
|
||||||
|
}
|
||||||
|
|
||||||
/// 当前 epoch 中已分配并跟踪的对象数量(未晋升到 session 的临时对象)。
|
/// 当前 epoch 中已分配并跟踪的对象数量(未晋升到 session 的临时对象)。
|
||||||
pub fn epoch_object_count(&self) -> usize {
|
pub fn epoch_object_count(&self) -> usize {
|
||||||
self.gc_state.epoch_object_ptrs.len()
|
self.gc_state.epoch_object_ptrs.len()
|
||||||
@@ -1517,10 +1537,16 @@ impl Vm {
|
|||||||
// inline 状态(非 GC 根),此时回收会把调用方 regs 中的活值当死值释放。
|
// inline 状态(非 GC 根),此时回收会把调用方 regs 中的活值当死值释放。
|
||||||
// 返回顶层后检查恢复,存活值此时已回拷为执行根。账目未超水位时仅
|
// 返回顶层后检查恢复,存活值此时已回拷为执行根。账目未超水位时仅
|
||||||
// 少量字段比较。
|
// 少量字段比较。
|
||||||
if self.native_call_depth == 0 && self.gc_state.session_bytes_allocated >= self.gc_state.string_gc_watermark
|
if self.native_call_depth == 0 {
|
||||||
{
|
// 峰值高水位:同一边界采样 session 堆账目上界
|
||||||
|
let bytes = self.gc_state.session_bytes_allocated;
|
||||||
|
if bytes > self.gc_state.session_bytes_peak {
|
||||||
|
self.gc_state.session_bytes_peak = bytes;
|
||||||
|
}
|
||||||
|
if bytes >= self.gc_state.string_gc_watermark {
|
||||||
self.maybe_collect_session_strings();
|
self.maybe_collect_session_strings();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if let Some(max_steps) = max_steps {
|
if let Some(max_steps) = max_steps {
|
||||||
if steps > max_steps {
|
if steps > max_steps {
|
||||||
vm_warn!("dispatch: step limit {} exceeded at pc={}", max_steps, self.pc);
|
vm_warn!("dispatch: step limit {} exceeded at pc={}", max_steps, self.pc);
|
||||||
|
|||||||
@@ -38,6 +38,9 @@ pub(crate) struct GcState {
|
|||||||
/// `cell.value` 中的对象引用),只在 full_reset 统一释放。
|
/// `cell.value` 中的对象引用),只在 full_reset 统一释放。
|
||||||
pub(crate) session_cell_ptrs: RefCell<Vec<*mut UpvalueCell>>,
|
pub(crate) session_cell_ptrs: RefCell<Vec<*mut UpvalueCell>>,
|
||||||
pub(crate) session_bytes_allocated: usize,
|
pub(crate) session_bytes_allocated: usize,
|
||||||
|
/// 执行期 session 堆账目的峰值高水位(`session_bytes_allocated` 的采样上界),
|
||||||
|
/// 顶层指令边界采样,全量重置清零。留存内存观测用。
|
||||||
|
pub(crate) session_bytes_peak: usize,
|
||||||
/// 执行期字符串 GC 的触发水位:本次收集后的存活字节 + 阈值增量。
|
/// 执行期字符串 GC 的触发水位:本次收集后的存活字节 + 阈值增量。
|
||||||
/// 仅当账目超过水位才在指令边界触发回收——活串超阈值时不会每指令重复
|
/// 仅当账目超过水位才在指令边界触发回收——活串超阈值时不会每指令重复
|
||||||
/// 触发无死串可回收的白跑,且保证触发点恒在无 builtin 局部活值的边界。
|
/// 触发无死串可回收的白跑,且保证触发点恒在无 builtin 局部活值的边界。
|
||||||
|
|||||||
@@ -100,6 +100,7 @@ impl Vm {
|
|||||||
session_bigint_ptrs: std::cell::RefCell::new(Vec::new()),
|
session_bigint_ptrs: std::cell::RefCell::new(Vec::new()),
|
||||||
session_cell_ptrs: std::cell::RefCell::new(Vec::new()),
|
session_cell_ptrs: std::cell::RefCell::new(Vec::new()),
|
||||||
session_bytes_allocated: 0,
|
session_bytes_allocated: 0,
|
||||||
|
session_bytes_peak: 0,
|
||||||
string_gc_watermark: gc_threshold,
|
string_gc_watermark: gc_threshold,
|
||||||
gc_threshold_cached: gc_threshold,
|
gc_threshold_cached: gc_threshold,
|
||||||
gc_watermark: gc_threshold,
|
gc_watermark: gc_threshold,
|
||||||
@@ -212,6 +213,7 @@ impl Vm {
|
|||||||
session_bigint_ptrs: std::cell::RefCell::new(Vec::new()),
|
session_bigint_ptrs: std::cell::RefCell::new(Vec::new()),
|
||||||
session_cell_ptrs: std::cell::RefCell::new(Vec::new()),
|
session_cell_ptrs: std::cell::RefCell::new(Vec::new()),
|
||||||
session_bytes_allocated: 0,
|
session_bytes_allocated: 0,
|
||||||
|
session_bytes_peak: 0,
|
||||||
string_gc_watermark: gc_threshold,
|
string_gc_watermark: gc_threshold,
|
||||||
gc_threshold_cached: gc_threshold,
|
gc_threshold_cached: gc_threshold,
|
||||||
gc_watermark: gc_threshold,
|
gc_watermark: gc_threshold,
|
||||||
@@ -329,6 +331,7 @@ impl Vm {
|
|||||||
self.gc_state.epoch_object_ptrs.clear();
|
self.gc_state.epoch_object_ptrs.clear();
|
||||||
self.gc_state.session_epoch.reset();
|
self.gc_state.session_epoch.reset();
|
||||||
self.gc_state.session_bytes_allocated = 0;
|
self.gc_state.session_bytes_allocated = 0;
|
||||||
|
self.gc_state.session_bytes_peak = 0;
|
||||||
self.gc_state.string_gc_watermark = self.kernel_core.config().session_gc_threshold;
|
self.gc_state.string_gc_watermark = self.kernel_core.config().session_gc_threshold;
|
||||||
self.gc_state.session_gc = crate::session_gc::SessionGc::new();
|
self.gc_state.session_gc = crate::session_gc::SessionGc::new();
|
||||||
self.symbols.reset();
|
self.symbols.reset();
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
// 数组增缩容量:填满 200000 元素后截回 1000,观测容量是否回落
|
||||||
|
// 堆账目按元素 Vec capacity 计——截短后若不收缩,留存账目仍反映大容量
|
||||||
|
var arr = [];
|
||||||
|
for (var i = 0; i < 200000; i++) {
|
||||||
|
arr[i] = i * 3 + 1;
|
||||||
|
}
|
||||||
|
arr.length = 1000;
|
||||||
|
arr.length
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
// 闭包/upvalue 长链留存:10000 层闭包,每层同时捕获前层闭包与自身 payload 对象,
|
||||||
|
// 自 head 沿 prev 链回溯第一层时全部驻留(函数对象、upvalue cell、payload 串)
|
||||||
|
var head = null;
|
||||||
|
for (var i = 0; i < 10000; i++) {
|
||||||
|
(function (prev, payload) {
|
||||||
|
head = function () {
|
||||||
|
return [prev, payload];
|
||||||
|
};
|
||||||
|
})(head, { id: i, data: "closure_payload_padding_" + i });
|
||||||
|
}
|
||||||
|
var cur = head;
|
||||||
|
var hops = 0;
|
||||||
|
while (cur !== null) {
|
||||||
|
cur = cur()[0];
|
||||||
|
hops++;
|
||||||
|
}
|
||||||
|
hops === 10000
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
// 跨调用长存活数据:全局 store 经 30000 次函数调用累积 30000 条记录,
|
||||||
|
// 记录在函数内构造、写出 global 后跨帧存活(session 堆)
|
||||||
|
var store = { records: [], total: 0 };
|
||||||
|
function logRecord(tag, value) {
|
||||||
|
var r = { seq: store.total, tag: tag, value: value };
|
||||||
|
store.records.push(r);
|
||||||
|
store.total++;
|
||||||
|
}
|
||||||
|
for (var i = 0; i < 30000; i++) {
|
||||||
|
logRecord("tag_" + (i % 16), "record_payload_" + i);
|
||||||
|
}
|
||||||
|
store.records.length
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// 原生盒边持有:Map 持 20000 组字符串键 + 对象值,
|
||||||
|
// 键串与值对象(及其 payload 串)全部经 Map 原生盒边驻留
|
||||||
|
var m = new Map();
|
||||||
|
for (var i = 0; i < 20000; i++) {
|
||||||
|
m.set("map_key_" + i, { id: i, data: "map_value_payload_" + i });
|
||||||
|
}
|
||||||
|
m.size
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
// 大型对象图留存:15 层满二叉树共 32767 个节点,顶层 root 整体驻留 session 堆
|
||||||
|
// 每节点 id 唯一(免意外共享),内节点带 left/right 子引用,叶节点带 leaf 标记
|
||||||
|
function makeNode(depth, base) {
|
||||||
|
if (depth === 0) {
|
||||||
|
return { id: base, leaf: 1 };
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
id: base,
|
||||||
|
depth: depth,
|
||||||
|
left: makeNode(depth - 1, base * 2),
|
||||||
|
right: makeNode(depth - 1, base * 2 + 1),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
var root = makeNode(14, 1);
|
||||||
|
root.left.id + root.right.right.id
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// 原生盒边持有:Set 持 20000 个对象,每个对象带 payload 字符串,
|
||||||
|
// 全部经 Set 原生盒边驻留
|
||||||
|
var s = new Set();
|
||||||
|
for (var i = 0; i < 20000; i++) {
|
||||||
|
s.add({ id: i, data: "set_value_payload_" + i });
|
||||||
|
}
|
||||||
|
s.size
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// 字符串累积留存:40000 个互不相同的字符串驻留数组
|
||||||
|
// 内容由索引推导(无去重可合并),数组本身经顶层 var 驻留
|
||||||
|
var pool = [];
|
||||||
|
for (var i = 0; i < 40000; i++) {
|
||||||
|
pool[i] = "str_" + i + "_" + (i * 7 % 997) + "_padding_padding_padding";
|
||||||
|
}
|
||||||
|
pool.length
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
// 拼接 rope 链留存:对 200 字节字面量重复二元拼接,形成深 Cons 链
|
||||||
|
// 每链节点账目按拼接逻辑长度计,报告值高于实际字节足迹;
|
||||||
|
// 链自顶层 var 驻留,节点地址稳定不搬移
|
||||||
|
var piece =
|
||||||
|
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
|
||||||
|
var chain = "seed_seed_seed";
|
||||||
|
for (var i = 0; i < 1000; i++) {
|
||||||
|
chain = chain + piece;
|
||||||
|
}
|
||||||
|
chain.length
|
||||||
Reference in New Issue
Block a user