diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 00000000..cb8a1f05 --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,7 @@ +{ + "permissions": { + "allow": [ + "Bash(cargo test:*)" + ] + } +} diff --git a/benchmarks/btreemap/canbench_results.yml b/benchmarks/btreemap/canbench_results.yml index 2dabed3e..55fef468 100644 --- a/benchmarks/btreemap/canbench_results.yml +++ b/benchmarks/btreemap/canbench_results.yml @@ -601,304 +601,325 @@ benches: heap_increase: 0 stable_memory_increase: 0 scopes: {} + btreemap_v2_heap_memory_used_blob_1024_128: + total: + calls: 1 + instructions: 207 + heap_increase: 0 + stable_memory_increase: 0 + scopes: {} + btreemap_v2_heap_memory_used_blob_4_128: + total: + calls: 1 + instructions: 207 + heap_increase: 0 + stable_memory_increase: 0 + scopes: {} + btreemap_v2_heap_memory_used_vec_32_128: + total: + calls: 1 + instructions: 207 + heap_increase: 0 + stable_memory_increase: 0 + scopes: {} btreemap_v2_insert_10mib_values: total: calls: 1 - instructions: 4388592719 + instructions: 4387226354 heap_increase: 161 stable_memory_increase: 3613 scopes: {} btreemap_v2_insert_blob8_u64: total: calls: 1 - instructions: 436633391 + instructions: 436629416 heap_increase: 0 stable_memory_increase: 4 scopes: {} btreemap_v2_insert_blob_1024_128: total: calls: 1 - instructions: 5496222167 + instructions: 5496218165 heap_increase: 0 stable_memory_increase: 196 scopes: {} btreemap_v2_insert_blob_128_128: total: calls: 1 - instructions: 1180838234 + instructions: 1180834208 heap_increase: 0 stable_memory_increase: 46 scopes: {} btreemap_v2_insert_blob_16_128: total: calls: 1 - instructions: 486249812 + instructions: 486246194 heap_increase: 0 stable_memory_increase: 24 scopes: {} btreemap_v2_insert_blob_256_128: total: calls: 1 - instructions: 1789038066 + instructions: 1789034049 heap_increase: 0 stable_memory_increase: 67 scopes: {} btreemap_v2_insert_blob_32_0: total: calls: 1 - instructions: 490459106 + instructions: 490455134 heap_increase: 0 stable_memory_increase: 8 scopes: {} btreemap_v2_insert_blob_32_1024: total: calls: 1 - instructions: 703500065 + instructions: 703496225 heap_increase: 0 stable_memory_increase: 173 scopes: {} btreemap_v2_insert_blob_32_128: total: calls: 1 - instructions: 542568143 + instructions: 542564351 heap_increase: 0 stable_memory_increase: 28 scopes: {} btreemap_v2_insert_blob_32_16: total: calls: 1 - instructions: 519117156 + instructions: 519113244 heap_increase: 0 stable_memory_increase: 11 scopes: {} btreemap_v2_insert_blob_32_256: total: calls: 1 - instructions: 570690137 + instructions: 570686267 heap_increase: 0 stable_memory_increase: 49 scopes: {} btreemap_v2_insert_blob_32_32: total: calls: 1 - instructions: 528951192 + instructions: 528947313 heap_increase: 0 stable_memory_increase: 13 scopes: {} btreemap_v2_insert_blob_32_4: total: calls: 1 - instructions: 509380036 + instructions: 509376109 heap_increase: 0 stable_memory_increase: 8 scopes: {} btreemap_v2_insert_blob_32_512: total: calls: 1 - instructions: 610063051 + instructions: 610059178 heap_increase: 0 stable_memory_increase: 91 scopes: {} btreemap_v2_insert_blob_32_64: total: calls: 1 - instructions: 534921249 + instructions: 534917409 heap_increase: 0 stable_memory_increase: 18 scopes: {} btreemap_v2_insert_blob_32_8: total: calls: 1 - instructions: 517593840 + instructions: 517590057 heap_increase: 0 stable_memory_increase: 9 scopes: {} btreemap_v2_insert_blob_4_128: total: calls: 1 - instructions: 407268940 + instructions: 407266843 heap_increase: 0 stable_memory_increase: 13 scopes: {} btreemap_v2_insert_blob_512_128: total: calls: 1 - instructions: 3041223382 + instructions: 3041219347 heap_increase: 0 stable_memory_increase: 111 scopes: {} btreemap_v2_insert_blob_64_128: total: calls: 1 - instructions: 661179485 + instructions: 661175552 heap_increase: 0 stable_memory_increase: 34 scopes: {} btreemap_v2_insert_blob_8_128: total: calls: 1 - instructions: 458615568 + instructions: 458612442 heap_increase: 0 stable_memory_increase: 20 scopes: {} btreemap_v2_insert_principal: total: calls: 1 - instructions: 503308324 + instructions: 503303086 heap_increase: 0 stable_memory_increase: 8 scopes: {} btreemap_v2_insert_u64_blob8: total: calls: 1 - instructions: 406744693 + instructions: 406739905 heap_increase: 0 stable_memory_increase: 5 scopes: {} btreemap_v2_insert_u64_u64: total: calls: 1 - instructions: 414580548 + instructions: 414575469 heap_increase: 0 stable_memory_increase: 6 scopes: {} btreemap_v2_insert_u64_vec8: total: calls: 1 - instructions: 410372254 + instructions: 410368222 heap_increase: 0 stable_memory_increase: 21 scopes: {} btreemap_v2_insert_vec8_u64: total: calls: 1 - instructions: 594103663 + instructions: 594100537 heap_increase: 0 stable_memory_increase: 16 scopes: {} btreemap_v2_insert_vec_1024_128: total: calls: 1 - instructions: 2744606200 + instructions: 2744513878 heap_increase: 0 stable_memory_increase: 193 scopes: {} btreemap_v2_insert_vec_128_128: total: calls: 1 - instructions: 1012643373 + instructions: 1012625574 heap_increase: 0 stable_memory_increase: 51 scopes: {} btreemap_v2_insert_vec_16_128: total: calls: 1 - instructions: 708997439 + instructions: 708984362 heap_increase: 0 stable_memory_increase: 31 scopes: {} btreemap_v2_insert_vec_256_128: total: calls: 1 - instructions: 1402219761 + instructions: 1402182234 heap_increase: 0 stable_memory_increase: 71 scopes: {} btreemap_v2_insert_vec_32_0: total: calls: 1 - instructions: 621849784 + instructions: 621845953 heap_increase: 0 stable_memory_increase: 20 scopes: {} btreemap_v2_insert_vec_32_1024: total: calls: 1 - instructions: 1182132701 + instructions: 1182054323 heap_increase: 0 stable_memory_increase: 171 scopes: {} btreemap_v2_insert_vec_32_128: total: calls: 1 - instructions: 756106158 + instructions: 756092289 heap_increase: 0 stable_memory_increase: 33 scopes: {} btreemap_v2_insert_vec_32_16: total: calls: 1 - instructions: 666191903 + instructions: 666188006 heap_increase: 0 stable_memory_increase: 20 scopes: {} btreemap_v2_insert_vec_32_256: total: calls: 1 - instructions: 869225881 + instructions: 869200138 heap_increase: 0 stable_memory_increase: 54 scopes: {} btreemap_v2_insert_vec_32_32: total: calls: 1 - instructions: 661645394 + instructions: 661641515 heap_increase: 0 stable_memory_increase: 20 scopes: {} btreemap_v2_insert_vec_32_4: total: calls: 1 - instructions: 660354036 + instructions: 660350181 heap_increase: 0 stable_memory_increase: 20 scopes: {} btreemap_v2_insert_vec_32_512: total: calls: 1 - instructions: 973664463 + instructions: 973623252 heap_increase: 0 stable_memory_increase: 91 scopes: {} btreemap_v2_insert_vec_32_64: total: calls: 1 - instructions: 691643081 + instructions: 691630844 heap_increase: 0 stable_memory_increase: 24 scopes: {} btreemap_v2_insert_vec_32_8: total: calls: 1 - instructions: 659845953 + instructions: 659842170 heap_increase: 0 stable_memory_increase: 20 scopes: {} btreemap_v2_insert_vec_4_128: total: calls: 1 - instructions: 604355928 + instructions: 604348722 heap_increase: 0 stable_memory_increase: 16 scopes: {} btreemap_v2_insert_vec_512_128: total: calls: 1 - instructions: 1859161681 + instructions: 1859108239 heap_increase: 0 stable_memory_increase: 112 scopes: {} btreemap_v2_insert_vec_64_128: total: calls: 1 - instructions: 846320282 + instructions: 846297779 heap_increase: 0 stable_memory_increase: 41 scopes: {} btreemap_v2_insert_vec_8_128: total: calls: 1 - instructions: 666289233 + instructions: 666278550 heap_increase: 0 stable_memory_increase: 23 scopes: {} @@ -975,658 +996,658 @@ benches: btreemap_v2_mem_manager_insert_blob512_u64: total: calls: 1 - instructions: 3127680452 + instructions: 3127693876 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_mem_manager_insert_u64_blob512: total: calls: 1 - instructions: 607288370 + instructions: 607302030 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_mem_manager_insert_u64_u64: total: calls: 1 - instructions: 520545864 + instructions: 520562305 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_mem_manager_insert_u64_vec512: total: calls: 1 - instructions: 834100595 + instructions: 834230066 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_mem_manager_insert_vec512_u64: total: calls: 1 - instructions: 1964405448 + instructions: 1964533302 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_mem_manager_remove_blob512_u64: total: calls: 1 - instructions: 4310221225 + instructions: 4310230683 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_mem_manager_remove_u64_blob512: total: calls: 1 - instructions: 882970074 + instructions: 882979670 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_mem_manager_remove_u64_u64: total: calls: 1 - instructions: 736785650 + instructions: 736797990 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_mem_manager_remove_u64_vec512: total: calls: 1 - instructions: 1223804753 + instructions: 1223992077 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_mem_manager_remove_vec512_u64: total: calls: 1 - instructions: 3084565257 + instructions: 3084753399 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_first_blob8_u64: total: calls: 1 - instructions: 599256189 + instructions: 599252070 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_first_blob_1024_128: total: calls: 1 - instructions: 8387602842 + instructions: 8387598840 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_first_blob_128_128: total: calls: 1 - instructions: 1824725965 + instructions: 1824721939 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_first_blob_16_128: total: calls: 1 - instructions: 743084112 + instructions: 743080488 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_first_blob_256_128: total: calls: 1 - instructions: 2763911828 + instructions: 2763907811 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_first_blob_32_0: total: calls: 1 - instructions: 751497584 + instructions: 751493486 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_first_blob_32_1024: total: calls: 1 - instructions: 1121481321 + instructions: 1121477481 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_first_blob_32_128: total: calls: 1 - instructions: 860285162 + instructions: 860281370 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_first_blob_32_16: total: calls: 1 - instructions: 795095715 + instructions: 795091803 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_first_blob_32_256: total: calls: 1 - instructions: 891739804 + instructions: 891735934 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_first_blob_32_32: total: calls: 1 - instructions: 808560505 + instructions: 808556626 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_first_blob_32_4: total: calls: 1 - instructions: 778864648 + instructions: 778860667 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_first_blob_32_512: total: calls: 1 - instructions: 957571175 + instructions: 957567308 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_first_blob_32_64: total: calls: 1 - instructions: 817406508 + instructions: 817402668 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_first_blob_32_8: total: calls: 1 - instructions: 796779002 + instructions: 796775177 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_first_blob_4_128: total: calls: 1 - instructions: 371435725 + instructions: 371433628 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_first_blob_512_128: total: calls: 1 - instructions: 4613700344 + instructions: 4613696303 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_first_blob_64_128: total: calls: 1 - instructions: 1031695256 + instructions: 1031691323 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_first_blob_8_128: total: calls: 1 - instructions: 604976184 + instructions: 604973040 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_first_principal: total: calls: 1 - instructions: 800191973 + instructions: 800186729 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_first_u64_blob8: total: calls: 1 - instructions: 669276877 + instructions: 669272017 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_first_u64_u64: total: calls: 1 - instructions: 681264462 + instructions: 681259383 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_first_u64_vec8: total: calls: 1 - instructions: 671829217 + instructions: 671825185 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_first_vec8_u64: total: calls: 1 - instructions: 782876135 + instructions: 782873009 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_first_vec_1024_128: total: calls: 1 - instructions: 4049265076 + instructions: 4049152840 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_first_vec_128_128: total: calls: 1 - instructions: 1503218744 + instructions: 1503200945 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_first_vec_16_128: total: calls: 1 - instructions: 1024772148 + instructions: 1024759071 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_first_vec_256_128: total: calls: 1 - instructions: 2019891066 + instructions: 2019833997 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_first_vec_32_0: total: calls: 1 - instructions: 862946599 + instructions: 862942768 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_first_vec_32_1024: total: calls: 1 - instructions: 1689829372 + instructions: 1689732148 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_first_vec_32_128: total: calls: 1 - instructions: 1088869698 + instructions: 1088853861 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_first_vec_32_16: total: calls: 1 - instructions: 928270806 + instructions: 928266909 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_first_vec_32_256: total: calls: 1 - instructions: 1215466767 + instructions: 1215441024 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_first_vec_32_32: total: calls: 1 - instructions: 931028208 + instructions: 931024329 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_first_vec_32_4: total: calls: 1 - instructions: 914745699 + instructions: 914741844 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_first_vec_32_512: total: calls: 1 - instructions: 1373268399 + instructions: 1373208474 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_first_vec_32_64: total: calls: 1 - instructions: 972735601 + instructions: 972723364 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_first_vec_32_8: total: calls: 1 - instructions: 924967663 + instructions: 924963880 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_first_vec_4_128: total: calls: 1 - instructions: 540984708 + instructions: 540977502 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_first_vec_512_128: total: calls: 1 - instructions: 2716330411 + instructions: 2716276969 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_first_vec_64_128: total: calls: 1 - instructions: 1234556907 + instructions: 1234517190 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_first_vec_8_128: total: calls: 1 - instructions: 850734232 + instructions: 850723549 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_last_blob8_u64: total: calls: 1 - instructions: 575661530 + instructions: 575657399 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_last_blob_1024_128: total: calls: 1 - instructions: 8055247791 + instructions: 8055243789 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_last_blob_128_128: total: calls: 1 - instructions: 1755509118 + instructions: 1755505092 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_last_blob_16_128: total: calls: 1 - instructions: 716556083 + instructions: 716552465 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_last_blob_256_128: total: calls: 1 - instructions: 2669730307 + instructions: 2669726290 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_last_blob_32_0: total: calls: 1 - instructions: 723335313 + instructions: 723331257 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_last_blob_32_1024: total: calls: 1 - instructions: 1085450731 + instructions: 1085446885 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_last_blob_32_128: total: calls: 1 - instructions: 825902306 + instructions: 825898514 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_last_blob_32_16: total: calls: 1 - instructions: 766901397 + instructions: 766897485 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_last_blob_32_256: total: calls: 1 - instructions: 860879651 + instructions: 860875781 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_last_blob_32_32: total: calls: 1 - instructions: 775936416 + instructions: 775932537 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_last_blob_32_4: total: calls: 1 - instructions: 755124599 + instructions: 755120630 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_last_blob_32_512: total: calls: 1 - instructions: 932320719 + instructions: 932316858 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_last_blob_32_64: total: calls: 1 - instructions: 791545690 + instructions: 791541850 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_last_blob_32_8: total: calls: 1 - instructions: 768161716 + instructions: 768157909 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_last_blob_4_128: total: calls: 1 - instructions: 357885021 + instructions: 357882924 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_last_blob_512_128: total: calls: 1 - instructions: 4444567386 + instructions: 4444563339 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_last_blob_64_128: total: calls: 1 - instructions: 999738711 + instructions: 999734778 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_last_blob_8_128: total: calls: 1 - instructions: 598942864 + instructions: 598939732 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_last_principal: total: calls: 1 - instructions: 780291969 + instructions: 780286737 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_last_u64_blob8: total: calls: 1 - instructions: 645812582 + instructions: 645807686 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_last_u64_u64: total: calls: 1 - instructions: 657455588 + instructions: 657450509 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_last_u64_vec8: total: calls: 1 - instructions: 648268033 + instructions: 648264001 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_last_vec8_u64: total: calls: 1 - instructions: 759547094 + instructions: 759543968 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_last_vec_1024_128: total: calls: 1 - instructions: 4254818123 + instructions: 4254705881 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_last_vec_128_128: total: calls: 1 - instructions: 1509650210 + instructions: 1509632411 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_last_vec_16_128: total: calls: 1 - instructions: 1006125814 + instructions: 1006112737 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_last_vec_256_128: total: calls: 1 - instructions: 2073363853 + instructions: 2073306796 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_last_vec_32_0: total: calls: 1 - instructions: 846109662 + instructions: 846105831 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_last_vec_32_1024: total: calls: 1 - instructions: 1664437667 + instructions: 1664340419 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_last_vec_32_128: total: calls: 1 - instructions: 1064611264 + instructions: 1064595433 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_last_vec_32_16: total: calls: 1 - instructions: 902974455 + instructions: 902970558 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_last_vec_32_256: total: calls: 1 - instructions: 1193452039 + instructions: 1193426296 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_last_vec_32_32: total: calls: 1 - instructions: 910490347 + instructions: 910486468 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_last_vec_32_4: total: calls: 1 - instructions: 898985596 + instructions: 898981741 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_last_vec_32_512: total: calls: 1 - instructions: 1356914135 + instructions: 1356854198 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_last_vec_32_64: total: calls: 1 - instructions: 949782755 + instructions: 949770518 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_last_vec_32_8: total: calls: 1 - instructions: 902473492 + instructions: 902469709 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_last_vec_4_128: total: calls: 1 - instructions: 527064939 + instructions: 527057733 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_last_vec_512_128: total: calls: 1 - instructions: 2808165041 + instructions: 2808111599 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_last_vec_64_128: total: calls: 1 - instructions: 1220824533 + instructions: 1220784792 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_last_vec_8_128: total: calls: 1 - instructions: 850619579 + instructions: 850608896 heap_increase: 0 stable_memory_increase: 0 scopes: {} @@ -1696,301 +1717,301 @@ benches: btreemap_v2_remove_10mib_values: total: calls: 1 - instructions: 4711118385 + instructions: 4709565702 heap_increase: 0 stable_memory_increase: 657 scopes: {} btreemap_v2_remove_blob8_u64: total: calls: 1 - instructions: 584636969 + instructions: 584632796 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_blob_1024_128: total: calls: 1 - instructions: 7363800309 + instructions: 7363796307 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_blob_128_128: total: calls: 1 - instructions: 1588756329 + instructions: 1588752303 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_blob_16_128: total: calls: 1 - instructions: 665337905 + instructions: 665334293 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_blob_256_128: total: calls: 1 - instructions: 2421697300 + instructions: 2421693283 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_blob_32_0: total: calls: 1 - instructions: 654250005 + instructions: 654246003 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_blob_32_1024: total: calls: 1 - instructions: 985325869 + instructions: 985322023 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_blob_32_128: total: calls: 1 - instructions: 746608890 + instructions: 746605092 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_blob_32_16: total: calls: 1 - instructions: 700059994 + instructions: 700056094 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_blob_32_256: total: calls: 1 - instructions: 784125701 + instructions: 784121825 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_blob_32_32: total: calls: 1 - instructions: 711919498 + instructions: 711915619 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_blob_32_4: total: calls: 1 - instructions: 696214241 + instructions: 696210302 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_blob_32_512: total: calls: 1 - instructions: 857936684 + instructions: 857932817 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_blob_32_64: total: calls: 1 - instructions: 737074307 + instructions: 737070467 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_blob_32_8: total: calls: 1 - instructions: 696057915 + instructions: 696054096 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_blob_4_128: total: calls: 1 - instructions: 452857933 + instructions: 452855812 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_blob_512_128: total: calls: 1 - instructions: 4071507249 + instructions: 4071503214 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_blob_64_128: total: calls: 1 - instructions: 910052271 + instructions: 910048338 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_blob_8_128: total: calls: 1 - instructions: 599299651 + instructions: 599296531 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_principal: total: calls: 1 - instructions: 684159422 + instructions: 684154256 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_u64_blob8: total: calls: 1 - instructions: 566079913 + instructions: 566075041 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_u64_u64: total: calls: 1 - instructions: 586718731 + instructions: 586713652 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_u64_vec8: total: calls: 1 - instructions: 571366355 + instructions: 571362323 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_vec8_u64: total: calls: 1 - instructions: 755823918 + instructions: 755820792 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_vec_1024_128: total: calls: 1 - instructions: 4475943466 + instructions: 4475823940 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_vec_128_128: total: calls: 1 - instructions: 1418213214 + instructions: 1418195415 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_vec_16_128: total: calls: 1 - instructions: 921111290 + instructions: 921098213 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_vec_256_128: total: calls: 1 - instructions: 2244668594 + instructions: 2244605357 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_vec_32_0: total: calls: 1 - instructions: 834330028 + instructions: 834326197 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_vec_32_1024: total: calls: 1 - instructions: 1701563272 + instructions: 1701459670 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_vec_32_128: total: calls: 1 - instructions: 1036968514 + instructions: 1036942087 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_vec_32_16: total: calls: 1 - instructions: 871328883 + instructions: 871324986 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_vec_32_256: total: calls: 1 - instructions: 1240176546 + instructions: 1240150803 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_vec_32_32: total: calls: 1 - instructions: 867569469 + instructions: 867565590 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_vec_32_4: total: calls: 1 - instructions: 862147420 + instructions: 862143565 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_vec_32_512: total: calls: 1 - instructions: 1405971132 + instructions: 1405903551 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_vec_32_64: total: calls: 1 - instructions: 968312981 + instructions: 968300744 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_vec_32_8: total: calls: 1 - instructions: 856477157 + instructions: 856473374 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_vec_4_128: total: calls: 1 - instructions: 662121914 + instructions: 662114708 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_vec_512_128: total: calls: 1 - instructions: 3082706271 + instructions: 3082652829 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_vec_64_128: total: calls: 1 - instructions: 1183958569 + instructions: 1183921648 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_vec_8_128: total: calls: 1 - instructions: 821774415 + instructions: 821763732 heap_increase: 0 stable_memory_increase: 0 scopes: {} @@ -2120,4 +2141,46 @@ benches: heap_increase: 0 stable_memory_increase: 0 scopes: {} + btreemap_v2_stable_memory_size_blob_1024_128: + total: + calls: 1 + instructions: 207 + heap_increase: 0 + stable_memory_increase: 0 + scopes: {} + btreemap_v2_stable_memory_size_blob_4_128: + total: + calls: 1 + instructions: 207 + heap_increase: 0 + stable_memory_increase: 0 + scopes: {} + btreemap_v2_stable_memory_size_vec_32_128: + total: + calls: 1 + instructions: 207 + heap_increase: 0 + stable_memory_increase: 0 + scopes: {} + btreemap_v2_stable_memory_used_blob_1024_128: + total: + calls: 1 + instructions: 207 + heap_increase: 0 + stable_memory_increase: 0 + scopes: {} + btreemap_v2_stable_memory_used_blob_4_128: + total: + calls: 1 + instructions: 207 + heap_increase: 0 + stable_memory_increase: 0 + scopes: {} + btreemap_v2_stable_memory_used_vec_32_128: + total: + calls: 1 + instructions: 207 + heap_increase: 0 + stable_memory_increase: 0 + scopes: {} version: 0.2.0 diff --git a/benchmarks/btreemap/src/main.rs b/benchmarks/btreemap/src/main.rs index 167b973c..945f6747 100644 --- a/benchmarks/btreemap/src/main.rs +++ b/benchmarks/btreemap/src/main.rs @@ -815,4 +815,52 @@ fn range_count_helper_v2(count: usize, size: usize) -> BenchResult { }) } +// Benchmarks for memory reporting methods. + +fn populate_btree_v2() -> BTreeMap { + let mut btree = BTreeMap::new(DefaultMemoryImpl::default()); + let mut rng = Rng::from_seed(0); + let items = generate_random_kv::(10_000, &mut rng); + for (k, v) in items { + btree.insert(k, v); + } + btree +} + +fn heap_memory_used_helper() -> BenchResult { + let btree = populate_btree_v2::(); + bench_fn(|| { + let _ = btree.heap_memory_used(); + }) +} + +fn stable_memory_size_helper() -> BenchResult { + let btree = populate_btree_v2::(); + bench_fn(|| { + let _ = btree.stable_memory_size(); + }) +} + +fn stable_memory_used_helper() -> BenchResult { + let btree = populate_btree_v2::(); + bench_fn(|| { + let _ = btree.stable_memory_used(); + }) +} + +bench_tests! { + // Small bounded, large bounded, unbounded. + btreemap_v2_heap_memory_used_blob_4_128, heap_memory_used_helper, Blob4, Blob128; + btreemap_v2_heap_memory_used_blob_1024_128, heap_memory_used_helper, Blob1024, Blob128; + btreemap_v2_heap_memory_used_vec_32_128, heap_memory_used_helper, UnboundedVecN32, UnboundedVecN128; + + btreemap_v2_stable_memory_size_blob_4_128, stable_memory_size_helper, Blob4, Blob128; + btreemap_v2_stable_memory_size_blob_1024_128, stable_memory_size_helper, Blob1024, Blob128; + btreemap_v2_stable_memory_size_vec_32_128, stable_memory_size_helper, UnboundedVecN32, UnboundedVecN128; + + btreemap_v2_stable_memory_used_blob_4_128, stable_memory_used_helper, Blob4, Blob128; + btreemap_v2_stable_memory_used_blob_1024_128, stable_memory_used_helper, Blob1024, Blob128; + btreemap_v2_stable_memory_used_vec_32_128, stable_memory_used_helper, UnboundedVecN32, UnboundedVecN128; +} + fn main() {} diff --git a/src/btreemap.rs b/src/btreemap.rs index c6f4ae92..fc4503fa 100644 --- a/src/btreemap.rs +++ b/src/btreemap.rs @@ -53,9 +53,10 @@ mod iter; mod node; use crate::btreemap::iter::{IterInternal, KeysIter, ValuesIter}; use crate::{ + mem_size::MemSize, storable::Bound as StorableBound, types::{Address, NULL}, - Memory, Storable, + Memory, Storable, WASM_PAGE_SIZE, }; use allocator::Allocator; pub use iter::Iter; @@ -252,6 +253,22 @@ where _phantom: PhantomData<(K, V)>, } +impl MemSize for BTreeMap +where + K: Storable + Ord + Clone, + V: Storable, + M: Memory, +{ + fn mem_size(&self) -> usize { + // Excludes _phantom (zero-size) and the Memory handle + // (reported separately via stable_memory_size). + self.root_addr.mem_size() + + self.version.mem_size() + + self.allocator.mem_size() + + self.length.mem_size() + } +} + #[derive(PartialEq, Debug)] /// The packed header size must be <= ALLOCATOR_OFFSET. struct BTreeHeader { @@ -392,6 +409,60 @@ where btree } + /// Returns the amount of heap and stack memory used in bytes. + pub fn heap_memory_used(&self) -> usize { + self.mem_size() + } + + /// Returns the amount of stable memory allocated in bytes. + pub fn stable_memory_size(&self) -> usize { + (self.allocator.memory().size() * WASM_PAGE_SIZE) as usize + } + + /// Returns the exact height of the tree by walking from root to a leaf. + /// + /// Returns 0 for an empty tree. Requires O(height) stable memory reads, + /// which is typically 3–5 for millions of entries. + #[cfg(test)] + fn height(&self) -> u32 { + if self.root_addr == NULL { + return 0; + } + let mut height = 1; + let mut node = self.load_node(self.root_addr); + while node.node_type() == NodeType::Internal { + node = self.load_node(node.child(0)); + height += 1; + } + height + } + + /// Estimates the tree height from the number of entries. O(1). + /// + /// Uses `ceil(log_B(n))` with an occupancy correction factor. + /// May differ from the actual height by ±1 in some cases. + pub fn height_approx(&self) -> u32 { + let n = self.length; + if n == 0 { + return 0; + } + // Average node occupancy after random inserts is ~69% (ln 2). + // Use B * 1.3 ≈ effective fan-out (conservative: slightly under ln 2). + let effective_b = (node::B as f64) * 1.3; + // max(1, ...) because a non-empty tree always has at least height 1. + std::cmp::max(1, (n as f64).log(effective_b).ceil() as u32) + } + + /// Returns the approximate amount of stable memory actually used in bytes. + pub fn stable_memory_used(&self) -> usize { + // BTreeMap header + allocator header + allocated chunks. + // O(1): uses the cached chunk count, no node traversal. + (ALLOCATOR_OFFSET as u64 + + Allocator::::header_size().get() + + self.allocator.num_allocated_chunks() * self.allocator.chunk_size().get()) + as usize + } + /// Loads the map from memory. pub fn load(memory: M) -> Self { Self::load_helper(memory, true) @@ -3378,4 +3449,161 @@ mod test { assert_eq!(btree.allocator.num_allocated_chunks(), 0); } + + #[test] + fn memory_reporting_empty() { + let mem = make_memory(); + let btree = BTreeMap::::new(mem); + + // BTreeMap holds Address(8) + Version(16) + Allocator fields(20) + u64(8) = 52 bytes. + // No heap allocations, so heap_memory_used == sum of inline field sizes. + // Update this value if the struct layout changes. + assert_eq!(btree.heap_memory_used(), 52); + + // One WASM page (64 KiB) is allocated on init. + assert_eq!(btree.stable_memory_size(), WASM_PAGE_SIZE as usize); + + // Only the BTreeMap header (52 bytes) + AllocatorHeader (48 bytes) = 100 bytes used. + let base_used = ALLOCATOR_OFFSET + Allocator::::header_size().get() as usize; + assert_eq!(base_used, 100); + assert_eq!(btree.stable_memory_used(), base_used); + } + + #[test] + fn memory_reporting_insert_and_remove() { + let mem = make_memory(); + let mut btree = BTreeMap::::new(mem); + + let base_used = btree.stable_memory_used(); + let chunk_size = btree.allocator.chunk_size().get() as usize; + + for i in 0..100u32 { + btree.insert(i, i); + } + + // 100 entries in a V2 BTreeMap produce 19 nodes (chunks). + let chunks = btree.allocator.num_allocated_chunks(); + assert_eq!(chunks, 19); + assert_eq!( + btree.stable_memory_used(), + base_used + chunks as usize * chunk_size + ); + assert!(btree.stable_memory_used() <= btree.stable_memory_size()); + + // After removing all entries, used drops back to base (0 allocated chunks). + for i in 0..100u32 { + btree.remove(&i); + } + assert_eq!(btree.allocator.num_allocated_chunks(), 0); + assert_eq!(btree.stable_memory_used(), base_used); + + // Stable memory size never shrinks — still one page. + assert_eq!(btree.stable_memory_size(), WASM_PAGE_SIZE as usize); + } + + /// Verifies memory reporting invariants for a given K/V combination: + /// - heap_memory_used is consistent across types (same struct layout) + /// - stable_memory_used grows after inserts and returns to base after removal + /// - stable_memory_used <= stable_memory_size always + fn memory_reporting_for_type() { + let mem = make_memory(); + let mut btree = BTreeMap::::new(mem); + + // All BTreeMap instances have the same struct layout regardless of K/V. + // Update this value if the struct layout changes. + assert_eq!(btree.heap_memory_used(), 52); + + let base_used = btree.stable_memory_used(); + let chunk_size = btree.allocator.chunk_size().get() as usize; + + // Insert entries. + for i in 0..50_u32 { + btree.insert(K::build(i), V::build(i)); + } + let chunks_after_insert = btree.allocator.num_allocated_chunks(); + assert!(chunks_after_insert > 0); + assert_eq!( + btree.stable_memory_used(), + base_used + chunks_after_insert as usize * chunk_size + ); + assert!(btree.stable_memory_used() <= btree.stable_memory_size()); + + // Remove all entries. + for i in 0..50_u32 { + btree.remove(&K::build(i)); + } + assert_eq!(btree.allocator.num_allocated_chunks(), 0); + assert_eq!(btree.stable_memory_used(), base_used); + } + + #[test] + fn memory_reporting_u32_u32() { + memory_reporting_for_type::(); + } + + #[test] + fn memory_reporting_blob10_blob20() { + memory_reporting_for_type::, Blob<20>>(); + } + + #[test] + fn memory_reporting_u32_unit() { + memory_reporting_for_type::(); + } + + #[test] + fn memory_reporting_vec_u8_vec_u8() { + memory_reporting_for_type::(); + } + + #[test] + fn memory_reporting_string_string() { + memory_reporting_for_type::(); + } + + #[test] + fn memory_reporting_blob10_vec_u8() { + memory_reporting_for_type::, MonotonicVec32>(); + } + + #[test] + fn height_empty() { + let mem = make_memory(); + let btree = BTreeMap::::new(mem); + assert_eq!(btree.height(), 0); + assert_eq!(btree.height_approx(), 0); + } + + #[test] + fn height_exact_vs_approx() { + let mem = make_memory(); + let mut btree = BTreeMap::::new(mem); + + // Insert items in batches and compare exact vs approximate height. + let test_sizes = [1, 5, 11, 12, 50, 100, 500, 1_000, 5_000, 10_000, 50_000, 100_000]; + + for &target in &test_sizes { + while btree.len() < target { + let i = btree.len() as u32; + btree.insert(i, ()); + } + + let exact = btree.height(); + let approx = btree.height_approx(); + let diff = (approx as i32) - (exact as i32); + + assert!( + exact > 0, + "n={target}: exact height should be > 0 after inserts" + ); + assert!( + diff.unsigned_abs() <= 1, + "n={target}: height_approx ({approx}) differs from \ + height ({exact}) by more than 1" + ); + } + + // Sanity: at 100k entries the tree should be reasonably short. + assert!(btree.height() <= 7); + } } diff --git a/src/btreemap/allocator.rs b/src/btreemap/allocator.rs index 68544599..229823ed 100644 --- a/src/btreemap/allocator.rs +++ b/src/btreemap/allocator.rs @@ -1,4 +1,5 @@ use crate::{ + mem_size::MemSize, read_struct, types::{Address, Bytes, NULL}, write_struct, Memory, @@ -45,6 +46,17 @@ pub struct Allocator { memory: M, } +impl MemSize for Allocator { + fn mem_size(&self) -> usize { + // Excludes `memory: M` — it's a handle to the backing store, + // not data owned by the allocator. + self.header_addr.mem_size() + + self.allocation_size.mem_size() + + self.num_allocated_chunks.mem_size() + + self.free_list_head.mem_size() + } +} + #[repr(C, packed)] #[derive(PartialEq, Debug)] struct AllocatorHeader { @@ -225,17 +237,21 @@ impl Allocator { write_struct(&header, self.header_addr, &self.memory); } - #[cfg(test)] - pub fn num_allocated_chunks(&self) -> u64 { + pub(crate) fn num_allocated_chunks(&self) -> u64 { self.num_allocated_chunks } - // The full size of a chunk, which is the size of the header + the `allocation_size` that's - // available to the user. - fn chunk_size(&self) -> Bytes { + /// The full size of a chunk, which is the size of the header + the `allocation_size` that's + /// available to the user. + pub(crate) fn chunk_size(&self) -> Bytes { self.allocation_size + ChunkHeader::size() } + /// Returns the size of the allocator header in bytes. + pub(crate) fn header_size() -> Bytes { + AllocatorHeader::size() + } + /// Destroys the allocator and returns the underlying memory. #[inline] pub fn into_memory(self) -> M { diff --git a/src/btreemap/node.rs b/src/btreemap/node.rs index 5daa0c40..89ec68c4 100644 --- a/src/btreemap/node.rs +++ b/src/btreemap/node.rs @@ -1,5 +1,6 @@ use crate::{ btreemap::Allocator, + mem_size::MemSize, read_address_vec, read_struct, read_to_vec, read_u32, read_u64, storable::Storable, types::{Address, Bytes}, @@ -18,7 +19,7 @@ use io::NodeReader; // The minimum degree to use in the btree. // This constant is taken from Rust's std implementation of BTreeMap. -const B: usize = 6; +pub(crate) const B: usize = 6; // The maximum number of entries per node. const CAPACITY: usize = 2 * B - 1; const LAYOUT_VERSION_1: u8 = 1; @@ -36,6 +37,12 @@ pub enum NodeType { Internal, } +impl MemSize for NodeType { + fn mem_size(&self) -> usize { + core::mem::size_of::() + } +} + pub type Entry = (K, Vec); pub type EntryRef<'a, K> = (&'a K, &'a [u8]); @@ -66,6 +73,17 @@ pub struct Node { overflows: Vec
, } +impl MemSize for Node { + fn mem_size(&self) -> usize { + self.address.mem_size() + + self.entries.mem_size() + + self.children.mem_size() + + self.node_type.mem_size() + + self.version.mem_size() + + self.overflows.mem_size() + } +} + impl Node { /// Loads a node from memory at the given address. pub fn load(address: Address, page_size: PageSize, memory: &M) -> Self { @@ -528,7 +546,7 @@ impl NodeHeader { /// A lazily-loaded object, which can be either an immediate value or a deferred reference. #[derive(Debug)] -enum LazyObject { +enum LazyObject { ByVal(T), ByRef { offset: Bytes, @@ -537,7 +555,31 @@ enum LazyObject { }, } -impl LazyObject { +impl LazyObject { + /// Reports the serialized data size for loaded values, or just the + /// fixed fields for unloaded references. Uses `Storable::to_bytes()` + /// to measure ByVal — avoids requiring `T: MemSize`. + fn mem_size(&self) -> usize { + match self { + LazyObject::ByVal(value) => value.to_bytes().len(), + LazyObject::ByRef { + offset, + size, + loaded, + } => { + offset.mem_size() + + size.mem_size() + + if loaded.get().is_some() { + *size as usize + } else { + 0 + } + } + } + } +} + +impl LazyObject { #[inline(always)] pub fn by_value(value: T) -> Self { LazyObject::ByVal(value) @@ -582,6 +624,12 @@ type Blob = Vec; #[derive(Debug)] struct LazyValue(LazyObject); +impl MemSize for LazyValue { + fn mem_size(&self) -> usize { + self.0.mem_size() + } +} + impl LazyValue { #[inline(always)] pub fn by_value(value: Blob) -> Self { @@ -605,9 +653,15 @@ impl LazyValue { } #[derive(Debug)] -struct LazyKey(LazyObject); +struct LazyKey(LazyObject); -impl LazyKey { +impl MemSize for LazyKey { + fn mem_size(&self) -> usize { + self.0.mem_size() + } +} + +impl LazyKey { #[inline(always)] pub fn by_value(value: K) -> Self { Self(LazyObject::by_value(value)) @@ -647,6 +701,15 @@ impl Version { } } +impl MemSize for Version { + fn mem_size(&self) -> usize { + match self { + Version::V1(page_size) => page_size.mem_size(), + Version::V2(page_size) => page_size.mem_size(), + } + } +} + /// The size of an individual page in the memory where nodes are stored. /// A node, if it's bigger than a single page, overflows into multiple pages. #[allow(dead_code)] @@ -669,6 +732,15 @@ impl PageSize { } } +impl MemSize for PageSize { + fn mem_size(&self) -> usize { + match self { + Self::Value(page_size) => page_size.mem_size(), + Self::Derived(derived) => derived.mem_size(), + } + } +} + /// A page size derived from the maximum sizes of the keys and values. #[derive(Debug, PartialEq, Copy, Clone, Eq)] pub struct DerivedPageSize { @@ -682,3 +754,9 @@ impl DerivedPageSize { v1::size_v1(self.max_key_size, self.max_value_size).get() as u32 } } + +impl MemSize for DerivedPageSize { + fn mem_size(&self) -> usize { + self.max_key_size.mem_size() + self.max_value_size.mem_size() + } +} diff --git a/src/btreemap/node/tests.rs b/src/btreemap/node/tests.rs index ff1158ca..622dac1e 100644 --- a/src/btreemap/node/tests.rs +++ b/src/btreemap/node/tests.rs @@ -309,3 +309,70 @@ fn can_call_node_value_multiple_times_on_same_index() { let value2 = node.value(0, &mem); assert_eq!(value1, value2); } + +mod mem_size_tests { + use super::*; + use crate::mem_size::MemSize; + + #[test] + fn lazy_value_by_value() { + // ByVal with a 5-byte blob: reports the serialized length. + let val = LazyValue::by_value(vec![1, 2, 3, 4, 5]); + assert_eq!(val.mem_size(), 5); + } + + #[test] + fn lazy_value_by_ref_not_loaded() { + // ByRef before loading: offset (8) + size (4) = 12, no data. + let val = LazyValue::by_ref(Bytes::new(100), 42); + assert_eq!(val.mem_size(), 8 + 4); + } + + #[test] + fn lazy_value_by_ref_loaded() { + // ByRef after loading: offset (8) + size (4) + data (42) = 54. + let val = LazyValue::by_ref(Bytes::new(100), 42); + val.get_or_load(|_, size| vec![0u8; size as usize]); + assert_eq!(val.mem_size(), 8 + 4 + 42); + } + + #[test] + fn lazy_key_u32_by_value() { + // u32 serializes to 4 bytes. + let key = LazyKey::::by_value(42); + assert_eq!(key.mem_size(), 4); + } + + #[test] + fn lazy_key_u32_by_ref_not_loaded() { + let key = LazyKey::::by_ref(Bytes::new(100), 4); + assert_eq!(key.mem_size(), 8 + 4); + } + + #[test] + fn lazy_key_u32_by_ref_loaded() { + let key = LazyKey::::by_ref(Bytes::new(100), 4); + key.get_or_load(|_, _| 42u32); + assert_eq!(key.mem_size(), 8 + 4 + 4); + } + + #[test] + fn lazy_key_vec_by_value() { + // Vec key with 10 bytes of data. + let key = LazyKey::>::by_value(vec![0u8; 10]); + assert_eq!(key.mem_size(), 10); + } + + #[test] + fn lazy_key_vec_by_ref_not_loaded() { + let key = LazyKey::>::by_ref(Bytes::new(100), 10); + assert_eq!(key.mem_size(), 8 + 4); + } + + #[test] + fn lazy_key_vec_by_ref_loaded() { + let key = LazyKey::>::by_ref(Bytes::new(100), 10); + key.get_or_load(|_, size| vec![0u8; size as usize]); + assert_eq!(key.mem_size(), 8 + 4 + 10); + } +} diff --git a/src/lib.rs b/src/lib.rs index 5b5a98fc..1d685f29 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,6 +8,7 @@ pub mod file_mem; #[cfg(target_arch = "wasm32")] mod ic0_memory; // Memory API for canisters. pub mod log; +mod mem_size; pub mod memory_manager; pub mod min_heap; pub mod reader; diff --git a/src/mem_size.rs b/src/mem_size.rs new file mode 100644 index 00000000..a12397a3 --- /dev/null +++ b/src/mem_size.rs @@ -0,0 +1,182 @@ +/// Estimates the in-process memory footprint of a value in bytes, +/// covering both inline (stack) and heap-allocated data. +/// +/// Reports the logical data size, not the physical layout. Alignment +/// padding and allocator overhead are intentionally excluded — the +/// goal is to measure how much memory the data itself occupies. +/// +/// ## Performance +/// +/// Primitives, strings, and fixed-size containers are always O(1). +/// +/// Vectors of types with a known `ELEMENT_SIZE` are also O(1), +/// using simple multiplication instead of traversal. +/// +/// Types with a variable or unknown element size fall back to +/// per-element iteration, which is O(n). Implement `ELEMENT_SIZE` +/// for your fixed-size types to stay on the fast path. +pub trait MemSize { + /// Fixed per-element size, if known at compile time. + /// When `Some`, `Vec::mem_size()` uses `len * ELEMENT_SIZE` (O(1)) + /// instead of iterating (O(n)). Defaults to `None`. + const ELEMENT_SIZE: Option = None; + + /// Returns the estimated memory footprint of this value in bytes. + fn mem_size(&self) -> usize; +} + +impl MemSize for () { + #[inline] + fn mem_size(&self) -> usize { + 0 + } +} + +impl MemSize for u8 { + const ELEMENT_SIZE: Option = Some(std::mem::size_of::()); + + #[inline] + fn mem_size(&self) -> usize { + std::mem::size_of::() + } +} + +impl MemSize for [u8] { + #[inline] + fn mem_size(&self) -> usize { + std::mem::size_of_val(self) + } +} + +impl MemSize for u32 { + const ELEMENT_SIZE: Option = Some(std::mem::size_of::()); + + #[inline] + fn mem_size(&self) -> usize { + std::mem::size_of::() + } +} + +impl MemSize for u64 { + const ELEMENT_SIZE: Option = Some(std::mem::size_of::()); + + #[inline] + fn mem_size(&self) -> usize { + std::mem::size_of::() + } +} + +impl MemSize for &str { + #[inline] + fn mem_size(&self) -> usize { + self.as_bytes().mem_size() + } +} + +impl MemSize for Vec { + #[inline] + fn mem_size(&self) -> usize { + let elements_size = match T::ELEMENT_SIZE { + Some(el_size) => self.len() * el_size, + None => self.iter().map(|x| x.mem_size()).sum::(), + }; + std::mem::size_of::() + elements_size + } +} + +impl MemSize for (A, B) { + #[inline] + fn mem_size(&self) -> usize { + self.0.mem_size() + self.1.mem_size() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_mem_size_unit() { + assert_eq!(().mem_size(), 0); + } + + #[test] + fn test_mem_size_u8() { + assert_eq!(0_u8.mem_size(), 1); + assert_eq!(42_u8.mem_size(), 1); + } + + #[test] + fn test_mem_size_u8_slice() { + let a: [u8; 0] = []; + assert_eq!(a.mem_size(), 0); + assert_eq!([1_u8].mem_size(), 1); + assert_eq!([1_u8, 2_u8].mem_size(), 2); + } + + #[test] + fn test_mem_size_u32() { + assert_eq!(0_u32.mem_size(), 4); + assert_eq!(42_u32.mem_size(), 4); + } + + #[test] + fn test_mem_size_u64() { + assert_eq!(0_u64.mem_size(), 8); + assert_eq!(42_u64.mem_size(), 8); + } + + #[test] + fn test_mem_size_u8_vec() { + let base = std::mem::size_of::>(); + assert_eq!(Vec::::from([]).mem_size(), base); + assert_eq!(Vec::::from([1]).mem_size(), base + 1); + assert_eq!(Vec::::from([1, 2]).mem_size(), base + 2); + } + + #[test] + fn test_mem_size_str() { + assert_eq!("a".mem_size(), 1); + assert_eq!("ab".mem_size(), 2); + } + + #[test] + fn test_mem_size_vec_with_element_size() { + // Vec uses the ELEMENT_SIZE fast path (Some(4)). + let base = std::mem::size_of::>(); + assert_eq!(Vec::::new().mem_size(), base); + assert_eq!(vec![1_u32].mem_size(), base + 4); + assert_eq!(vec![1_u32, 2, 3].mem_size(), base + 12); + } + + #[test] + fn test_mem_size_vec_without_element_size() { + type Blob = Vec; + // Vec has ELEMENT_SIZE = None, falls back to per-element iteration. + let base = std::mem::size_of::>(); + let blob_base = std::mem::size_of::(); + assert_eq!(Vec::::new().mem_size(), base); + // Each Blob reports size_of::() + content_len. + assert_eq!(vec![Blob::from([1, 2])].mem_size(), base + blob_base + 2); + assert_eq!( + vec![Blob::from([1]), Blob::from([2, 3])].mem_size(), + base + 2 * blob_base + 3 + ); + } + + #[test] + fn test_mem_size_tuple() { + // Measures logical data size, not physical layout. + // (u32, u64) = 4 + 8 = 12 bytes (padding excluded). + assert_eq!((1_u32, 2_u64).mem_size(), 4 + 8); + } + + #[test] + fn test_element_size_constants() { + assert_eq!(::ELEMENT_SIZE, Some(1)); + assert_eq!(::ELEMENT_SIZE, Some(4)); + assert_eq!(::ELEMENT_SIZE, Some(8)); + // Vec has no fixed element size. + assert_eq!( as MemSize>::ELEMENT_SIZE, None); + } +} diff --git a/src/types.rs b/src/types.rs index 0c9bbf7f..3a3aacc9 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,3 +1,4 @@ +use crate::mem_size::MemSize; use core::ops::{Add, AddAssign, Div, Mul, Rem, Sub, SubAssign}; pub const NULL: Address = Address(0); @@ -53,6 +54,16 @@ impl AddAssign for Address { } } +impl MemSize for Address { + const ELEMENT_SIZE: Option = Some(std::mem::size_of::()); + + #[inline] + fn mem_size(&self) -> usize { + let val = self.0; + val.mem_size() + } +} + #[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Ord, Eq)] pub struct Bytes(u64); @@ -140,6 +151,16 @@ impl SubAssign for Bytes { } } +impl MemSize for Bytes { + const ELEMENT_SIZE: Option = Some(std::mem::size_of::()); + + #[inline] + fn mem_size(&self) -> usize { + let val = self.0; + val.mem_size() + } +} + impl Bytes { pub const fn new(val: u64) -> Self { Self(val)