diff --git a/benchmarks/btreemap/canbench_results.yml b/benchmarks/btreemap/canbench_results.yml index d90f6dcd..9af9c9a6 100644 --- a/benchmarks/btreemap/canbench_results.yml +++ b/benchmarks/btreemap/canbench_results.yml @@ -2,1099 +2,1099 @@ benches: btreemap_v2_contains_10mib_values: total: calls: 1 - instructions: 142210325 + instructions: 18483721 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_contains_blob_256_128: total: calls: 1 - instructions: 1310685287 + instructions: 898074463 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_contains_blob_32_0: total: calls: 1 - instructions: 329571719 + instructions: 286876333 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_contains_blob_32_1024: total: calls: 1 - instructions: 326072097 + instructions: 280845034 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_contains_blob_32_128: total: calls: 1 - instructions: 326242314 + instructions: 240212893 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_contains_blob_8_128: total: calls: 1 - instructions: 268086207 + instructions: 189757294 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_contains_principal: total: calls: 1 - instructions: 349062610 + instructions: 248326038 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_contains_u64_u64: total: calls: 1 - instructions: 223284326 + instructions: 169813378 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_contains_vec_32_128: total: calls: 1 - instructions: 428325937 + instructions: 293090409 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_contains_vec_32_vec128: total: calls: 1 - instructions: 428325937 + instructions: 293090409 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_first_key_value_blob_256_128: total: calls: 1 - instructions: 662680455 + instructions: 352584649 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_first_key_value_blob_32_0: total: calls: 1 - instructions: 288090371 + instructions: 100509231 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_first_key_value_blob_32_1024: total: calls: 1 - instructions: 445360530 + instructions: 255015339 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_first_key_value_blob_32_128: total: calls: 1 - instructions: 275320356 + instructions: 106205810 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_first_key_value_blob_8_128: total: calls: 1 - instructions: 291920456 + instructions: 55702017 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_first_key_value_principal: total: calls: 1 - instructions: 367230468 + instructions: 31523019 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_first_key_value_u64_u64: total: calls: 1 - instructions: 231140468 + instructions: 84894284 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_first_key_value_vec_32_128: total: calls: 1 - instructions: 227140383 + instructions: 38655866 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_first_key_value_vec_32_vec128: total: calls: 1 - instructions: 227140383 + instructions: 38655866 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_get_100k_u64_u64: total: calls: 1 - instructions: 2770647816 + instructions: 2324488694 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_get_10mib_values: total: calls: 1 - instructions: 388592804 + instructions: 264853978 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_get_blob8_u64: total: calls: 1 - instructions: 299599641 + instructions: 205954574 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_get_blob_1024_128: total: calls: 1 - instructions: 4457685507 + instructions: 2973112544 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_get_blob_128_128: total: calls: 1 - instructions: 855273807 + instructions: 575786182 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_get_blob_16_128: total: calls: 1 - instructions: 308463753 + instructions: 222873051 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_get_blob_256_128: total: calls: 1 - instructions: 1367052477 + instructions: 903185650 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_get_blob_32_0: total: calls: 1 - instructions: 337429950 + instructions: 288586363 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_get_blob_32_1024: total: calls: 1 - instructions: 350508333 + instructions: 292713740 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_get_blob_32_128: total: calls: 1 - instructions: 341653262 + instructions: 244952082 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_get_blob_32_16: total: calls: 1 - instructions: 330651190 + instructions: 249242168 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_get_blob_32_256: total: calls: 1 - instructions: 342643724 + instructions: 249314946 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_get_blob_32_32: total: calls: 1 - instructions: 342697250 + instructions: 247473705 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_get_blob_32_4: total: calls: 1 - instructions: 334169026 + instructions: 248138409 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_get_blob_32_512: total: calls: 1 - instructions: 342695399 + instructions: 257516792 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_get_blob_32_64: total: calls: 1 - instructions: 336813935 + instructions: 248375748 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_get_blob_32_8: total: calls: 1 - instructions: 335925065 + instructions: 245327388 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_get_blob_4_128: total: calls: 1 - instructions: 254906111 + instructions: 168233768 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_get_blob_512_128: total: calls: 1 - instructions: 2378828514 + instructions: 1589182180 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_get_blob_64_128: total: calls: 1 - instructions: 428744853 + instructions: 336202166 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_get_blob_8_128: total: calls: 1 - instructions: 279716343 + instructions: 194846097 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_get_miss_u64_u64: total: calls: 1 - instructions: 229192451 + instructions: 177058170 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_get_miss_zipf_10k_u64_u64: total: calls: 1 - instructions: 232121753 + instructions: 170849126 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_get_principal: total: calls: 1 - instructions: 356428202 + instructions: 250186765 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_get_u64_blob8: total: calls: 1 - instructions: 226446414 + instructions: 193975365 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_get_u64_u64: total: calls: 1 - instructions: 230534752 + instructions: 174490279 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_get_u64_vec8: total: calls: 1 - instructions: 226935610 + instructions: 167443479 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_get_vec8_u64: total: calls: 1 - instructions: 378429625 + instructions: 270920846 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_get_vec_1024_128: total: calls: 1 - instructions: 1869582398 + instructions: 1401866850 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_get_vec_128_128: total: calls: 1 - instructions: 576003245 + instructions: 479287247 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_get_vec_16_128: total: calls: 1 - instructions: 448512365 + instructions: 344463484 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_get_vec_256_128: total: calls: 1 - instructions: 904445652 + instructions: 646324172 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_get_vec_32_0: total: calls: 1 - instructions: 358216658 + instructions: 273323059 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_get_vec_32_1024: total: calls: 1 - instructions: 544469486 + instructions: 445969681 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_get_vec_32_128: total: calls: 1 - instructions: 436037967 + instructions: 299430441 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_get_vec_32_16: total: calls: 1 - instructions: 373713570 + instructions: 308866094 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_get_vec_32_256: total: calls: 1 - instructions: 447151619 + instructions: 327674384 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_get_vec_32_32: total: calls: 1 - instructions: 361330699 + instructions: 268350949 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_get_vec_32_4: total: calls: 1 - instructions: 358348309 + instructions: 270096896 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_get_vec_32_512: total: calls: 1 - instructions: 472992093 + instructions: 345969254 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_get_vec_32_64: total: calls: 1 - instructions: 404811886 + instructions: 289651598 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_get_vec_32_8: total: calls: 1 - instructions: 358456622 + instructions: 266782433 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_get_vec_4_128: total: calls: 1 - instructions: 410118909 + instructions: 208489486 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_get_vec_512_128: total: calls: 1 - instructions: 1254926041 + instructions: 1031162081 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_get_vec_64_128: total: calls: 1 - instructions: 500462810 + instructions: 402810590 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_get_vec_8_128: total: calls: 1 - instructions: 402273333 + instructions: 281643619 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_get_zipf_100k_u64_u64: total: calls: 1 - instructions: 2675648987 + instructions: 1985797141 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_get_zipf_10k_u64_u64: total: calls: 1 - instructions: 222259253 + instructions: 140000904 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_get_zipf_heavy_10k_u64_u64: total: calls: 1 - instructions: 214740278 + instructions: 87479763 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_insert_100k_u64_u64: total: calls: 1 - instructions: 4772156774 + instructions: 4771575582 heap_increase: 0 stable_memory_increase: 60 scopes: {} btreemap_v2_insert_10mib_values: total: calls: 1 - instructions: 4389918848 + instructions: 4388594466 heap_increase: 161 stable_memory_increase: 3613 scopes: {} btreemap_v2_insert_blob8_u64: total: calls: 1 - instructions: 437829679 + instructions: 437518117 heap_increase: 0 stable_memory_increase: 4 scopes: {} btreemap_v2_insert_blob_1024_128: total: calls: 1 - instructions: 5497499463 + instructions: 5497155337 heap_increase: 0 stable_memory_increase: 196 scopes: {} btreemap_v2_insert_blob_128_128: total: calls: 1 - instructions: 1182116392 + instructions: 1181773320 heap_increase: 0 stable_memory_increase: 46 scopes: {} btreemap_v2_insert_blob_16_128: total: calls: 1 - instructions: 487491246 + instructions: 487161376 heap_increase: 0 stable_memory_increase: 24 scopes: {} btreemap_v2_insert_blob_256_128: total: calls: 1 - instructions: 1790317122 + instructions: 1789972362 heap_increase: 0 stable_memory_increase: 67 scopes: {} btreemap_v2_insert_blob_32_0: total: calls: 1 - instructions: 491719539 + instructions: 491382348 heap_increase: 0 stable_memory_increase: 8 scopes: {} btreemap_v2_insert_blob_32_1024: total: calls: 1 - instructions: 704762555 + instructions: 704424311 heap_increase: 0 stable_memory_increase: 173 scopes: {} btreemap_v2_insert_blob_32_128: total: calls: 1 - instructions: 543826879 + instructions: 543489222 heap_increase: 0 stable_memory_increase: 28 scopes: {} btreemap_v2_insert_blob_32_16: total: calls: 1 - instructions: 520386697 + instructions: 520045244 heap_increase: 0 stable_memory_increase: 11 scopes: {} btreemap_v2_insert_blob_32_256: total: calls: 1 - instructions: 571955067 + instructions: 571615430 heap_increase: 0 stable_memory_increase: 49 scopes: {} btreemap_v2_insert_blob_32_32: total: calls: 1 - instructions: 530217284 + instructions: 529878058 heap_increase: 0 stable_memory_increase: 13 scopes: {} btreemap_v2_insert_blob_32_4: total: calls: 1 - instructions: 510643371 + instructions: 510305152 heap_increase: 0 stable_memory_increase: 8 scopes: {} btreemap_v2_insert_blob_32_512: total: calls: 1 - instructions: 611325945 + instructions: 610988635 heap_increase: 0 stable_memory_increase: 91 scopes: {} btreemap_v2_insert_blob_32_64: total: calls: 1 - instructions: 536185339 + instructions: 535845644 heap_increase: 0 stable_memory_increase: 18 scopes: {} btreemap_v2_insert_blob_32_8: total: calls: 1 - instructions: 518851674 + instructions: 518514257 heap_increase: 0 stable_memory_increase: 9 scopes: {} btreemap_v2_insert_blob_4_128: total: calls: 1 - instructions: 408387747 + instructions: 408095693 heap_increase: 0 stable_memory_increase: 13 scopes: {} btreemap_v2_insert_blob_512_128: total: calls: 1 - instructions: 3042503672 + instructions: 3042158781 heap_increase: 0 stable_memory_increase: 111 scopes: {} btreemap_v2_insert_blob_64_128: total: calls: 1 - instructions: 662452699 + instructions: 662108927 heap_increase: 0 stable_memory_increase: 34 scopes: {} btreemap_v2_insert_blob_8_128: total: calls: 1 - instructions: 459828006 + instructions: 459499652 heap_increase: 0 stable_memory_increase: 20 scopes: {} btreemap_v2_insert_overwrite_u64_u64: total: calls: 1 - instructions: 361059370 + instructions: 361032092 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_insert_overwrite_zipf_10k_u64_u64: total: calls: 1 - instructions: 350858333 + instructions: 350854126 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_insert_principal: total: calls: 1 - instructions: 504575702 + instructions: 502955163 heap_increase: 0 stable_memory_increase: 8 scopes: {} btreemap_v2_insert_seq_u64_u64: total: calls: 1 - instructions: 474490740 + instructions: 474435598 heap_increase: 0 stable_memory_increase: 8 scopes: {} btreemap_v2_insert_u64_blob8: total: calls: 1 - instructions: 407705243 + instructions: 407657131 heap_increase: 0 stable_memory_increase: 5 scopes: {} btreemap_v2_insert_u64_u64: total: calls: 1 - instructions: 415539341 + instructions: 415493019 heap_increase: 0 stable_memory_increase: 6 scopes: {} btreemap_v2_insert_u64_vec8: total: calls: 1 - instructions: 411333408 + instructions: 411284692 heap_increase: 0 stable_memory_increase: 21 scopes: {} btreemap_v2_insert_vec8_u64: total: calls: 1 - instructions: 594023125 + instructions: 593697044 heap_increase: 0 stable_memory_increase: 16 scopes: {} btreemap_v2_insert_vec_1024_128: total: calls: 1 - instructions: 2744190469 + instructions: 2744207295 heap_increase: 0 stable_memory_increase: 193 scopes: {} btreemap_v2_insert_vec_128_128: total: calls: 1 - instructions: 1012362500 + instructions: 1012252802 heap_increase: 0 stable_memory_increase: 51 scopes: {} btreemap_v2_insert_vec_16_128: total: calls: 1 - instructions: 708867453 + instructions: 708593539 heap_increase: 0 stable_memory_increase: 31 scopes: {} btreemap_v2_insert_vec_256_128: total: calls: 1 - instructions: 1401837524 + instructions: 1401822357 heap_increase: 0 stable_memory_increase: 71 scopes: {} btreemap_v2_insert_vec_32_0: total: calls: 1 - instructions: 623110632 + instructions: 621454653 heap_increase: 0 stable_memory_increase: 20 scopes: {} btreemap_v2_insert_vec_32_1024: total: calls: 1 - instructions: 1183237599 + instructions: 1181733986 heap_increase: 0 stable_memory_increase: 171 scopes: {} btreemap_v2_insert_vec_32_128: total: calls: 1 - instructions: 757287028 + instructions: 755701316 heap_increase: 0 stable_memory_increase: 33 scopes: {} btreemap_v2_insert_vec_32_16: total: calls: 1 - instructions: 667460509 + instructions: 665792239 heap_increase: 0 stable_memory_increase: 20 scopes: {} btreemap_v2_insert_vec_32_256: total: calls: 1 - instructions: 870346642 + instructions: 868829647 heap_increase: 0 stable_memory_increase: 54 scopes: {} btreemap_v2_insert_vec_32_32: total: calls: 1 - instructions: 662911486 + instructions: 661249696 heap_increase: 0 stable_memory_increase: 20 scopes: {} btreemap_v2_insert_vec_32_4: total: calls: 1 - instructions: 661617466 + instructions: 659952868 heap_increase: 0 stable_memory_increase: 20 scopes: {} btreemap_v2_insert_vec_32_512: total: calls: 1 - instructions: 974762564 + instructions: 973265238 heap_increase: 0 stable_memory_increase: 91 scopes: {} btreemap_v2_insert_vec_32_64: total: calls: 1 - instructions: 692893240 + instructions: 691239859 heap_increase: 0 stable_memory_increase: 24 scopes: {} btreemap_v2_insert_vec_32_8: total: calls: 1 - instructions: 661103717 + instructions: 659445704 heap_increase: 0 stable_memory_increase: 20 scopes: {} btreemap_v2_insert_vec_4_128: total: calls: 1 - instructions: 604207482 + instructions: 603957592 heap_increase: 0 stable_memory_increase: 16 scopes: {} btreemap_v2_insert_vec_512_128: total: calls: 1 - instructions: 1858741744 + instructions: 1858766935 heap_increase: 0 stable_memory_increase: 112 scopes: {} btreemap_v2_insert_vec_64_128: total: calls: 1 - instructions: 846140634 + instructions: 845917944 heap_increase: 0 stable_memory_increase: 41 scopes: {} btreemap_v2_insert_vec_8_128: total: calls: 1 - instructions: 666160448 + instructions: 665880865 heap_increase: 0 stable_memory_increase: 23 scopes: {} btreemap_v2_last_key_value_blob_256_128: total: calls: 1 - instructions: 641730455 + instructions: 150712776 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_last_key_value_blob_32_0: total: calls: 1 - instructions: 220890383 + instructions: 32538678 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_last_key_value_blob_32_1024: total: calls: 1 - instructions: 389710530 + instructions: 168949491 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_last_key_value_blob_32_128: total: calls: 1 - instructions: 266910368 + instructions: 122354174 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_last_key_value_blob_8_128: total: calls: 1 - instructions: 321080468 + instructions: 52855855 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_last_key_value_principal: total: calls: 1 - instructions: 217810468 + instructions: 32058181 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_last_key_value_u64_u64: total: calls: 1 - instructions: 245280513 + instructions: 81195505 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_last_key_value_vec_32_128: total: calls: 1 - instructions: 221130353 + instructions: 73273192 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_last_key_value_vec_32_vec128: total: calls: 1 - instructions: 221130353 + instructions: 73273192 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_mem_manager_get_blob512_u64: total: calls: 1 - instructions: 2458034243 + instructions: 1643937985 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_mem_manager_get_u64_blob512: total: calls: 1 - instructions: 295406794 + instructions: 212669038 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_mem_manager_get_u64_u64: total: calls: 1 - instructions: 291697340 + instructions: 213126867 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_mem_manager_get_u64_vec512: total: calls: 1 - instructions: 388255294 + instructions: 275679057 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_mem_manager_get_vec512_u64: total: calls: 1 - instructions: 1241928836 + instructions: 938992119 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_mem_manager_insert_blob512_u64: total: calls: 1 - instructions: 3127471916 + instructions: 3128595310 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_mem_manager_insert_u64_blob512: total: calls: 1 - instructions: 607078432 + instructions: 608184530 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_mem_manager_insert_u64_u64: total: calls: 1 - instructions: 520319877 + instructions: 521422074 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_mem_manager_insert_u64_vec512: total: calls: 1 - instructions: 833436222 + instructions: 834542320 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_mem_manager_insert_vec512_u64: total: calls: 1 - instructions: 1963926760 + instructions: 1965050153 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_mem_manager_remove_blob512_u64: total: calls: 1 - instructions: 4309922967 + instructions: 4317258317 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_mem_manager_remove_u64_blob512: total: calls: 1 - instructions: 882653107 + instructions: 884272624 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_mem_manager_remove_u64_u64: total: calls: 1 - instructions: 736484369 + instructions: 740550754 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_mem_manager_remove_u64_vec512: total: calls: 1 - instructions: 1222999902 + instructions: 1223807613 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_mem_manager_remove_vec512_u64: total: calls: 1 - instructions: 3084546334 + instructions: 3089893641 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_first_blob_256_128: total: calls: 1 - instructions: 2733250958 + instructions: 2560845497 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_first_blob_32_0: total: calls: 1 - instructions: 755069525 + instructions: 673848385 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_first_blob_32_1024: total: calls: 1 - instructions: 1087422287 + instructions: 1010250000 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_first_blob_32_128: total: calls: 1 - instructions: 850665025 + instructions: 763853329 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_first_blob_8_128: total: calls: 1 - instructions: 597743734 + instructions: 530703399 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_first_principal: total: calls: 1 - instructions: 804201522 + instructions: 705521741 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_first_u64_u64: total: calls: 1 - instructions: 678306722 + instructions: 604780824 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_first_vec_32_128: total: calls: 1 - instructions: 1070741361 + instructions: 1004082314 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_first_vec_32_vec128: total: calls: 1 - instructions: 1070741361 + instructions: 1004082314 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_last_blob_256_128: total: calls: 1 - instructions: 2639178653 + instructions: 2468529864 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_last_blob_32_0: total: calls: 1 - instructions: 727022722 + instructions: 644483819 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_last_blob_32_1024: total: calls: 1 - instructions: 1051364306 + instructions: 976881917 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_last_blob_32_128: total: calls: 1 - instructions: 816444045 + instructions: 732588393 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_last_blob_8_128: total: calls: 1 - instructions: 591603162 + instructions: 520663045 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_last_principal: total: calls: 1 - instructions: 784260250 + instructions: 691071371 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_last_u64_u64: total: calls: 1 - instructions: 654455143 + instructions: 585118539 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_last_vec_32_128: total: calls: 1 - instructions: 1045591253 + instructions: 978641304 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_pop_last_vec_32_vec128: total: calls: 1 - instructions: 1045591253 + instructions: 978641304 heap_increase: 0 stable_memory_increase: 0 scopes: {} @@ -1143,336 +1143,336 @@ benches: btreemap_v2_range_small_u64_u64: total: calls: 1 - instructions: 18468455 + instructions: 18488651 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_range_value_sum_1k_0b: total: calls: 1 - instructions: 17281 + instructions: 17283 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_range_value_sum_1k_10kib: total: calls: 1 - instructions: 20674718 + instructions: 20675386 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_range_value_sum_20_10mib: total: calls: 1 - instructions: 398305318 + instructions: 398305332 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_100k_u64_u64: total: calls: 1 - instructions: 6936906906 + instructions: 6999842110 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_10mib_values: total: calls: 1 - instructions: 4711740758 + instructions: 4711120592 heap_increase: 0 stable_memory_increase: 657 scopes: {} btreemap_v2_remove_blob8_u64: total: calls: 1 - instructions: 586603282 + instructions: 587268040 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_blob_1024_128: total: calls: 1 - instructions: 7366227460 + instructions: 7373819089 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_blob_128_128: total: calls: 1 - instructions: 1591130828 + instructions: 1590993931 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_blob_16_128: total: calls: 1 - instructions: 667569961 + instructions: 667616835 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_blob_256_128: total: calls: 1 - instructions: 2424117639 + instructions: 2424506478 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_blob_32_0: total: calls: 1 - instructions: 656626331 + instructions: 656932576 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_blob_32_1024: total: calls: 1 - instructions: 987650107 + instructions: 985492060 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_blob_32_128: total: calls: 1 - instructions: 748930270 + instructions: 748442255 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_blob_32_16: total: calls: 1 - instructions: 702341625 + instructions: 703334735 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_blob_32_256: total: calls: 1 - instructions: 786392550 + instructions: 785755544 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_blob_32_32: total: calls: 1 - instructions: 714208062 + instructions: 714498658 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_blob_32_4: total: calls: 1 - instructions: 698530472 + instructions: 700105939 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_blob_32_512: total: calls: 1 - instructions: 860239527 + instructions: 859791629 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_blob_32_64: total: calls: 1 - instructions: 739423065 + instructions: 739850689 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_blob_32_8: total: calls: 1 - instructions: 698331371 + instructions: 699332234 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_blob_4_128: total: calls: 1 - instructions: 454202403 + instructions: 454080937 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_blob_512_128: total: calls: 1 - instructions: 4073876476 + instructions: 4076894035 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_blob_64_128: total: calls: 1 - instructions: 912331275 + instructions: 912179724 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_blob_8_128: total: calls: 1 - instructions: 601322906 + instructions: 601498122 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_principal: total: calls: 1 - instructions: 686590606 + instructions: 687140958 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_u64_blob8: total: calls: 1 - instructions: 567859464 + instructions: 568774157 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_u64_u64: total: calls: 1 - instructions: 588546853 + instructions: 590493180 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_u64_vec8: total: calls: 1 - instructions: 573147088 + instructions: 573898992 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_vec8_u64: total: calls: 1 - instructions: 756005620 + instructions: 758654927 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_vec_1024_128: total: calls: 1 - instructions: 4501955326 + instructions: 4479866545 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_vec_128_128: total: calls: 1 - instructions: 1419196035 + instructions: 1435734364 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_vec_16_128: total: calls: 1 - instructions: 921541160 + instructions: 918990323 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_vec_256_128: total: calls: 1 - instructions: 2245385936 + instructions: 2245118577 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_vec_32_0: total: calls: 1 - instructions: 836646994 + instructions: 836642124 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_vec_32_1024: total: calls: 1 - instructions: 1703447808 + instructions: 1696990832 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_vec_32_128: total: calls: 1 - instructions: 1039788869 + instructions: 1037310067 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_vec_32_16: total: calls: 1 - instructions: 873604949 + instructions: 871791830 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_vec_32_256: total: calls: 1 - instructions: 1242209782 + instructions: 1253957475 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_vec_32_32: total: calls: 1 - instructions: 869860278 + instructions: 868943787 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_vec_32_4: total: calls: 1 - instructions: 864469775 + instructions: 865634258 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_vec_32_512: total: calls: 1 - instructions: 1407927685 + instructions: 1405270381 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_vec_32_64: total: calls: 1 - instructions: 970869819 + instructions: 969446133 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_vec_32_8: total: calls: 1 - instructions: 858773574 + instructions: 858349641 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_vec_4_128: total: calls: 1 - instructions: 662592339 + instructions: 660487971 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_vec_512_128: total: calls: 1 - instructions: 3083368088 + instructions: 3083463895 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_vec_64_128: total: calls: 1 - instructions: 1184614604 + instructions: 1183678367 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_remove_vec_8_128: total: calls: 1 - instructions: 822773389 + instructions: 821888435 heap_increase: 0 stable_memory_increase: 0 scopes: {} @@ -1563,42 +1563,42 @@ benches: btreemap_v2_scan_values_1k_0b: total: calls: 1 - instructions: 1235822 + instructions: 1237822 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_scan_values_1k_10kib: total: calls: 1 - instructions: 56745109 + instructions: 56747109 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_scan_values_20_10mib: total: calls: 1 - instructions: 1103710993 + instructions: 1103711033 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_scan_values_rev_1k_0b: total: calls: 1 - instructions: 1232328 + instructions: 1234328 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_scan_values_rev_1k_10kib: total: calls: 1 - instructions: 56683937 + instructions: 56685937 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreemap_v2_scan_values_rev_20_10mib: total: calls: 1 - instructions: 1103710424 + instructions: 1103710464 heap_increase: 0 stable_memory_increase: 0 scopes: {} diff --git a/benchmarks/btreeset/canbench_results.yml b/benchmarks/btreeset/canbench_results.yml index 8c79367a..7031b5e1 100644 --- a/benchmarks/btreeset/canbench_results.yml +++ b/benchmarks/btreeset/canbench_results.yml @@ -2,70 +2,70 @@ benches: btreeset_insert_blob_1024: total: calls: 1 - instructions: 7265892372 + instructions: 7266956788 heap_increase: 1 stable_memory_increase: 256 scopes: {} btreeset_insert_blob_128: total: calls: 1 - instructions: 1636767402 + instructions: 1637831818 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreeset_insert_blob_16: total: calls: 1 - instructions: 715664326 + instructions: 716728742 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreeset_insert_blob_256: total: calls: 1 - instructions: 2445225121 + instructions: 2446289537 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreeset_insert_blob_32: total: calls: 1 - instructions: 814718710 + instructions: 815783126 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreeset_insert_blob_512: total: calls: 1 - instructions: 4049839436 + instructions: 4050903852 heap_increase: 0 stable_memory_increase: 128 scopes: {} btreeset_insert_blob_64: total: calls: 1 - instructions: 980469261 + instructions: 981533677 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreeset_insert_blob_8: total: calls: 1 - instructions: 694043361 + instructions: 695107777 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreeset_insert_u32: total: calls: 1 - instructions: 540164863 + instructions: 541229279 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreeset_insert_u64: total: calls: 1 - instructions: 561497942 + instructions: 562562358 heap_increase: 0 stable_memory_increase: 0 scopes: {} @@ -492,70 +492,70 @@ benches: btreeset_remove_blob_1024: total: calls: 1 - instructions: 7722749146 + instructions: 7723769472 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreeset_remove_blob_128: total: calls: 1 - instructions: 1666415454 + instructions: 1667435780 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreeset_remove_blob_16: total: calls: 1 - instructions: 705483928 + instructions: 706481770 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreeset_remove_blob_256: total: calls: 1 - instructions: 2534126706 + instructions: 2535147032 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreeset_remove_blob_32: total: calls: 1 - instructions: 802114863 + instructions: 803118519 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreeset_remove_blob_512: total: calls: 1 - instructions: 4261462921 + instructions: 4262483247 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreeset_remove_blob_64: total: calls: 1 - instructions: 987128732 + instructions: 988152388 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreeset_remove_blob_8: total: calls: 1 - instructions: 683601373 + instructions: 684602545 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreeset_remove_u32: total: calls: 1 - instructions: 528154844 + instructions: 529154304 heap_increase: 0 stable_memory_increase: 0 scopes: {} btreeset_remove_u64: total: calls: 1 - instructions: 553060549 + instructions: 554060009 heap_increase: 0 stable_memory_increase: 0 scopes: {} diff --git a/benchmarks/io_chunks/canbench_results.yml b/benchmarks/io_chunks/canbench_results.yml index b9b59d80..4112f5c6 100644 --- a/benchmarks/io_chunks/canbench_results.yml +++ b/benchmarks/io_chunks/canbench_results.yml @@ -2,21 +2,21 @@ benches: read_chunks_btreemap_1: total: calls: 1 - instructions: 148723585 + instructions: 148723350 heap_increase: 1601 stable_memory_increase: 0 scopes: {} read_chunks_btreemap_1k: total: calls: 1 - instructions: 498228206 + instructions: 203378266 heap_increase: 0 stable_memory_increase: 0 scopes: {} read_chunks_btreemap_1m: total: calls: 1 - instructions: 40940569325 + instructions: 17808065697 heap_increase: 0 stable_memory_increase: 0 scopes: {} @@ -65,21 +65,21 @@ benches: write_chunks_btreemap_1: total: calls: 1 - instructions: 360207245 + instructions: 357205465 heap_increase: 13 stable_memory_increase: 1536 scopes: {} write_chunks_btreemap_1k: total: calls: 1 - instructions: 4194582593 + instructions: 4187223807 heap_increase: 2 stable_memory_increase: 1536 scopes: {} write_chunks_btreemap_1m: total: calls: 1 - instructions: 83673829307 + instructions: 83761099339 heap_increase: 0 stable_memory_increase: 3072 scopes: {} @@ -107,21 +107,21 @@ benches: write_chunks_vec_1: total: calls: 1 - instructions: 549903446 + instructions: 549903461 heap_increase: 0 stable_memory_increase: 1536 scopes: {} write_chunks_vec_1k: total: calls: 1 - instructions: 562132513 + instructions: 562145515 heap_increase: 0 stable_memory_increase: 1536 scopes: {} write_chunks_vec_1m: total: calls: 1 - instructions: 1771593099 + instructions: 1784593101 heap_increase: 0 stable_memory_increase: 1536 scopes: {} diff --git a/benchmarks/nns/canbench_results.yml b/benchmarks/nns/canbench_results.yml index 4186b23d..95d38b97 100644 --- a/benchmarks/nns/canbench_results.yml +++ b/benchmarks/nns/canbench_results.yml @@ -2,42 +2,42 @@ benches: vote_cascading_heap_centralized_10k: total: calls: 1 - instructions: 77758460 + instructions: 77755739 heap_increase: 16 stable_memory_increase: 0 scopes: {} vote_cascading_heap_centralized_1k: total: calls: 1 - instructions: 7815132 + instructions: 7816288 heap_increase: 1 stable_memory_increase: 0 scopes: {} vote_cascading_heap_chain_10k_15: total: calls: 1 - instructions: 1272207053 + instructions: 1272077976 heap_increase: 10 stable_memory_increase: 0 scopes: {} vote_cascading_heap_chain_10k_5: total: calls: 1 - instructions: 239648964 + instructions: 239410927 heap_increase: 10 stable_memory_increase: 0 scopes: {} vote_cascading_heap_chain_1k_15: total: calls: 1 - instructions: 124352570 + instructions: 124847103 heap_increase: 1 stable_memory_increase: 0 scopes: {} vote_cascading_heap_chain_1k_5: total: calls: 1 - instructions: 23867558 + instructions: 23958834 heap_increase: 0 stable_memory_increase: 0 scopes: {} @@ -58,56 +58,56 @@ benches: vote_cascading_stable_centralized_10k: total: calls: 1 - instructions: 1375501036 + instructions: 1372396240 heap_increase: 10 stable_memory_increase: 0 scopes: {} vote_cascading_stable_centralized_1k: total: calls: 1 - instructions: 99996435 + instructions: 99761831 heap_increase: 1 stable_memory_increase: 0 scopes: {} vote_cascading_stable_chain_10k_15: total: calls: 1 - instructions: 9819218496 + instructions: 9854978344 heap_increase: 5 stable_memory_increase: 0 scopes: {} vote_cascading_stable_chain_10k_5: total: calls: 1 - instructions: 3007431336 + instructions: 2924577184 heap_increase: 5 stable_memory_increase: 0 scopes: {} vote_cascading_stable_chain_1k_15: total: calls: 1 - instructions: 867330736 + instructions: 866392564 heap_increase: 0 stable_memory_increase: 0 scopes: {} vote_cascading_stable_chain_1k_5: total: calls: 1 - instructions: 253041123 + instructions: 251866705 heap_increase: 0 stable_memory_increase: 0 scopes: {} vote_cascading_stable_single_vote_10k: total: calls: 1 - instructions: 91466 + instructions: 91198 heap_increase: 0 stable_memory_increase: 0 scopes: {} vote_cascading_stable_single_vote_1k: total: calls: 1 - instructions: 66840 + instructions: 66620 heap_increase: 0 stable_memory_increase: 0 scopes: {} diff --git a/benchmarks/nns/src/nns_vote_cascading/tests.rs b/benchmarks/nns/src/nns_vote_cascading/tests.rs index a6b7fd2f..2c26b725 100644 --- a/benchmarks/nns/src/nns_vote_cascading/tests.rs +++ b/benchmarks/nns/src/nns_vote_cascading/tests.rs @@ -168,7 +168,7 @@ fn set_up_worst_case( for neuron_id in num_followees..num_neurons { let previous_neuron_ids = (neuron_id - num_half_followees - 1)..neuron_id; let followee_neuron_ids = previous_neuron_ids - .map(|id| NeuronId::from(id)) + .map(NeuronId::from) .chain(not_voting_neuron_ids.clone().into_iter()) .collect::>(); neuron_store.set_followees( diff --git a/src/btreemap.rs b/src/btreemap.rs index 12838476..80d1dea6 100644 --- a/src/btreemap.rs +++ b/src/btreemap.rs @@ -51,6 +51,7 @@ mod allocator; mod iter; mod node; +mod node_cache; use crate::btreemap::iter::{IterInternal, KeysIter, ValuesIter}; use crate::{ storable::Bound as StorableBound, @@ -60,7 +61,10 @@ use crate::{ use allocator::Allocator; pub use iter::Iter; use node::{DerivedPageSize, Entry, Node, NodeType, PageSize, Version}; +use node_cache::NodeCache; +pub use node_cache::NodeCacheMetrics; use std::borrow::Cow; +use std::cell::RefCell; use std::marker::PhantomData; use std::ops::{Bound, RangeBounds}; @@ -81,6 +85,15 @@ const DEFAULT_PAGE_SIZE: u32 = 1024; // A marker to indicate that the `PageSize` stored in the header is a `PageSize::Value`. const PAGE_SIZE_VALUE_MARKER: u32 = u32::MAX; +/// Default number of slots in the direct-mapped node cache. +/// +/// 16 slots cover the top two tree levels (1 root + up to 12 children = +/// 13 nodes) while keeping heap usage modest. +/// +/// Users can adjust via [`BTreeMap::with_node_cache`] or +/// [`BTreeMap::node_cache_resize`], including setting to 0 to disable. +const DEFAULT_NODE_CACHE_NUM_SLOTS: usize = 16; + /// A B-Tree map implementation that stores its data into a designated memory. /// /// # Memory Implementations @@ -248,6 +261,9 @@ where // The number of elements in the map. length: u64, + // Direct-mapped node cache to avoid re-loading hot nodes from stable memory. + cache: RefCell>, + // A marker to communicate to the Rust compiler that we own these types. _phantom: PhantomData<(K, V)>, } @@ -289,6 +305,112 @@ where } } + /// Configures the number of node-cache slots during construction. + /// + /// The cache is a direct-mapped cache that keeps frequently accessed + /// B-tree nodes in heap memory to avoid repeated stable-memory reads. + /// Each slot can hold one deserialized node; on collision, shallower + /// nodes (closer to the root) are kept over deeper ones. + /// + /// Pass `0` to disable the cache (the default). + /// + /// The top 2 levels of the tree contain 13 nodes (1 root + up to + /// 12 children). **16** slots is the smallest power of two that + /// covers them, but a direct-mapped cache is sensitive to address + /// collisions, so **32** is a safer default that leaves headroom + /// and typically gives 2 cache hits per operation regardless of + /// tree size. Prefer powers of two for efficient slot indexing. + /// + /// # Examples + /// + /// ```rust + /// use ic_stable_structures::{BTreeMap, DefaultMemoryImpl}; + /// + /// let map: BTreeMap = + /// BTreeMap::init(DefaultMemoryImpl::default()) + /// .with_node_cache(32); + /// ``` + pub fn with_node_cache(mut self, num_slots: usize) -> Self { + self.node_cache_resize(num_slots); + self + } + + /// Resizes the node cache at runtime. + /// + /// Existing cache contents and performance counters are discarded. + /// + /// Pass `0` to disable the cache entirely. + /// + /// See [`with_node_cache`](Self::with_node_cache) for guidance on + /// choosing a size. + /// + /// # Examples + /// + /// ```rust + /// use ic_stable_structures::{BTreeMap, DefaultMemoryImpl}; + /// + /// let mut map: BTreeMap = + /// BTreeMap::init(DefaultMemoryImpl::default()) + /// .with_node_cache(32); + /// + /// // After observing a poor hit ratio, grow the cache. + /// if map.node_cache_metrics().hit_ratio() < 0.5 { + /// map.node_cache_resize(64); + /// } + /// ``` + pub fn node_cache_resize(&mut self, num_slots: usize) { + *self.cache.get_mut() = NodeCache::new(self.version.page_size().get(), num_slots); + } + + /// Returns the current number of slots in the node cache. + /// + /// Returns `0` when the cache is disabled. + pub fn node_cache_size(&self) -> usize { + self.cache.borrow().num_slots() + } + + /// Evicts all cached nodes and resets metrics, keeping the + /// current cache capacity. + pub fn node_cache_clear(&mut self) { + self.cache.get_mut().clear(); + } + + /// Resets cache metrics (hit/miss counters) without evicting + /// cached nodes. + pub fn node_cache_clear_metrics(&mut self) { + self.cache.get_mut().clear_metrics(); + } + + /// Returns node-cache performance metrics. + /// + /// # Examples + /// + /// ```rust + /// use ic_stable_structures::{BTreeMap, DefaultMemoryImpl}; + /// + /// let mut map: BTreeMap = + /// BTreeMap::init(DefaultMemoryImpl::default()) + /// .with_node_cache(32); + /// map.insert(1, 100); + /// let _ = map.get(&1); + /// + /// let metrics = map.node_cache_metrics(); + /// println!("hit ratio: {:.1}%", metrics.hit_ratio() * 100.0); + /// ``` + pub fn node_cache_metrics(&self) -> NodeCacheMetrics { + self.cache.borrow().metrics() + } + + /// Returns a rough estimate of the cache's heap usage in bytes. + /// + /// Actual usage depends on key size and how many slots are + /// occupied. Treat this as an order-of-magnitude guide, not a + /// precise budget. + pub fn node_cache_size_bytes_approx(&self) -> usize { + self.cache.borrow().num_slots() + * (self.version.page_size().get() as usize + NodeCache::::slot_size()) + } + /// Initializes a v1 `BTreeMap`. /// /// This is exposed only in testing. @@ -358,6 +480,10 @@ where ), version: Version::V2(page_size), length: 0, + cache: RefCell::new(NodeCache::new( + page_size.get(), + DEFAULT_NODE_CACHE_NUM_SLOTS, + )), _phantom: PhantomData, }; @@ -373,6 +499,11 @@ where let max_key_size = K::BOUND.max_size(); let max_value_size = V::BOUND.max_size(); + let version = Version::V1(DerivedPageSize { + max_key_size, + max_value_size, + }); + let btree = Self { root_addr: NULL, allocator: Allocator::new( @@ -380,11 +511,12 @@ where Address::from(ALLOCATOR_OFFSET as u64), Node::::max_size(max_key_size, max_value_size), ), - version: Version::V1(DerivedPageSize { - max_key_size, - max_value_size, - }), + version, length: 0, + cache: RefCell::new(NodeCache::new( + version.page_size().get(), + DEFAULT_NODE_CACHE_NUM_SLOTS, + )), _phantom: PhantomData, }; @@ -434,6 +566,10 @@ where allocator: Allocator::load(memory, allocator_addr), version, length: header.length, + cache: RefCell::new(NodeCache::new( + version.page_size().get(), + DEFAULT_NODE_CACHE_NUM_SLOTS, + )), _phantom: PhantomData, } } @@ -653,8 +789,8 @@ where if self.root_addr == NULL { return None; } - self.traverse(self.root_addr, key, |node, idx| { - node.extract_entry_at(idx, self.memory()).1 // Extract value. + self.traverse(self.root_addr, 0, key, |node, idx| { + node.read_value_uncached(idx, self.memory()) }) .map(Cow::Owned) .map(V::from_bytes) @@ -663,80 +799,88 @@ where /// Returns true if the key exists. pub fn contains_key(&self, key: &K) -> bool { // An empty closure returns Some(()) if the key is found. - self.root_addr != NULL && self.traverse(self.root_addr, key, |_, _| ()).is_some() + self.root_addr != NULL && self.traverse(self.root_addr, 0, key, |_, _| ()).is_some() } /// Recursively traverses from `node_addr`, invoking `f` if `key` is found. Stops at a leaf if not. - fn traverse(&self, node_addr: Address, key: &K, f: F) -> Option + /// + /// Uses the node cache: nodes are taken out before use and returned after. + /// `depth` is the distance from the root (root = 0). + fn traverse(&self, node_addr: Address, depth: u8, key: &K, f: F) -> Option where - F: Fn(&mut Node, usize) -> R, + F: Fn(&Node, usize) -> R, { - let mut node = self.load_node(node_addr); - // Look for the key in the current node. + let node = self.take_or_load_node(node_addr); match node.search(key, self.memory()) { - Ok(idx) => Some(f(&mut node, idx)), // Key found: apply `f`. + Ok(idx) => { + let result = f(&node, idx); // Key found: apply `f`. + self.return_node(node, depth); + Some(result) + } Err(idx) => match node.node_type() { - NodeType::Leaf => None, // At a leaf: key not present. - NodeType::Internal => self.traverse(node.child(idx), key, f), // Continue search in child. + NodeType::Leaf => { + self.return_node(node, depth); + None // At a leaf: key not present. + } + NodeType::Internal => { + let child_addr = node.child(idx); + self.return_node(node, depth); + self.traverse(child_addr, depth.saturating_add(1), key, f) + } }, } } - /// Traverses to the min or max leaf and extracts a result using the provided closure. - fn get_min_or_max(&self, node: &Node, is_min: bool, extract: F) -> R + /// A helper function to find either the first or last entry in the tree, depending on the `is_first` flag. + fn find_first_or_last(&self, node: &Node, is_first: bool, depth: u8, extract: F) -> R where F: Fn(&Node, usize, &M) -> R, { - let mut current; - let mut current_ref = node; - loop { - match current_ref.node_type() { - NodeType::Leaf => { - let idx = if is_min { - 0 - } else { - // Last entry index in a 0-based array of entries. - current_ref.num_entries() - 1 - }; - return extract(current_ref, idx, self.memory()); - } - NodeType::Internal => { - let child_addr = if is_min { - current_ref.child(0) - } else { - // Last child index in a 0-based array of children. - current_ref.child(current_ref.children_len() - 1) - }; - current = self.load_node(child_addr); - current_ref = ¤t; - } + match node.node_type() { + NodeType::Leaf => { + let idx = if is_first { + 0 + } else { + // Last entry index in a 0-based array of entries. + node.num_entries() - 1 + }; + extract(node, idx, self.memory()) + } + NodeType::Internal => { + let child_addr = if is_first { + node.child(0) + } else { + // Last child index in a 0-based array of children. + node.child(node.children_len() - 1) + }; + let child = self.take_or_load_node(child_addr); + let new_depth = depth.saturating_add(1); + let result = self.find_first_or_last(&child, is_first, new_depth, extract); + self.return_node(child, new_depth); + result } } } #[inline(always)] fn first_key_inner(&self, node: &Node) -> K { - self.get_min_or_max(node, true, |n, i, m| n.key(i, m).clone()) + self.find_first_or_last(node, true, 0, |n, i, m| n.key(i, m).clone()) } #[inline(always)] fn last_key_inner(&self, node: &Node) -> K { - self.get_min_or_max(node, false, |n, i, m| n.key(i, m).clone()) + self.find_first_or_last(node, false, 0, |n, i, m| n.key(i, m).clone()) } #[inline(always)] fn first_entry_inner(&self, node: &Node) -> Entry { - self.get_min_or_max(node, true, |n, i, m| { - let (k, v) = n.entry(i, m); - (k.clone(), v.to_vec()) - }) + self.find_first_or_last(node, true, 0, |n, i, m| n.get_key_read_value_uncached(i, m)) } #[inline(always)] fn last_entry_inner(&self, node: &Node) -> Entry { - self.get_min_or_max(node, false, |n, i, m| { - let (k, v) = n.entry(i, m); - (k.clone(), v.to_vec()) + self.find_first_or_last(node, false, 0, |n, i, m| { + n.get_key_read_value_uncached(i, m) }) } @@ -771,6 +915,8 @@ where self.root_addr = NULL; self.length = 0; self.allocator.clear(); + let num_slots = self.cache.get_mut().num_slots(); + *self.cache.get_mut() = NodeCache::new(self.version.page_size().get(), num_slots); self.save_header(); } @@ -780,8 +926,9 @@ where if self.root_addr == NULL { return None; } - let root = self.load_node(self.root_addr); + let root = self.take_or_load_node(self.root_addr); let (k, encoded_v) = self.first_entry_inner(&root); + self.return_node(root, 0); Some((k, V::from_bytes(Cow::Owned(encoded_v)))) } @@ -791,8 +938,9 @@ where if self.root_addr == NULL { return None; } - let root = self.load_node(self.root_addr); + let root = self.take_or_load_node(self.root_addr); let (k, encoded_v) = self.last_entry_inner(&root); + self.return_node(root, 0); Some((k, V::from_bytes(Cow::Owned(encoded_v)))) } @@ -823,9 +971,9 @@ where } let root = self.load_node(self.root_addr); - let max_key = self.last_key_inner(&root); - self.remove_helper(root, &max_key) - .map(|v| (max_key, V::from_bytes(Cow::Owned(v)))) + let last_key = self.last_key_inner(&root); + self.remove_helper(root, &last_key) + .map(|v| (last_key, V::from_bytes(Cow::Owned(v)))) } /// Removes and returns the first element in the map. The key of this element is the minimum key that was in the map @@ -835,9 +983,9 @@ where } let root = self.load_node(self.root_addr); - let min_key = self.first_key_inner(&root); - self.remove_helper(root, &min_key) - .map(|v| (min_key, V::from_bytes(Cow::Owned(v)))) + let first_key = self.first_key_inner(&root); + self.remove_helper(root, &first_key) + .map(|v| (first_key, V::from_bytes(Cow::Owned(v)))) } /// A helper method for recursively removing a key from the B-tree. @@ -1331,7 +1479,13 @@ where /// [1, 2, 3, 4, 5, 6, 7] (stored in the `into` node) /// `source` is deallocated. fn merge(&mut self, source: Node, mut into: Node, median: Entry) -> Node { + let source_addr = source.address(); into.merge(source, median, &mut self.allocator); + // Node::merge saves `into` and deallocates `source` directly through + // the allocator, so we must invalidate both cache slots here. + let cache = self.cache.get_mut(); + cache.invalidate(into.address()); + cache.invalidate(source_addr); into } @@ -1343,22 +1497,46 @@ where } } - /// Deallocates a node. + /// Deallocates a node and invalidates its cache slot. #[inline] fn deallocate_node(&mut self, node: Node) { + let addr = node.address(); node.deallocate(self.allocator_mut()); + self.cache.get_mut().invalidate(addr); + } + + /// Takes a node from the cache, or loads it from memory if not cached. + /// + /// Used by read paths (`&self`). The caller must call `return_node` when + /// done to put the node back into the cache. + #[inline(always)] + fn take_or_load_node(&self, address: Address) -> Node { + if let Some(node) = self.cache.borrow_mut().take(address) { + return node; + } + Node::load(address, self.version.page_size(), self.memory()) + } + + /// Returns a node to the cache after use on a read path. + /// + /// `depth` is the distance from the root (root = 0), used by the + /// cache eviction policy. + #[inline(always)] + fn return_node(&self, node: Node, depth: u8) { + self.cache.borrow_mut().put(node.address(), node, depth); } - /// Loads a node from memory. + /// Loads a node from memory, bypassing the cache. #[inline] fn load_node(&self, address: Address) -> Node { Node::load(address, self.version.page_size(), self.memory()) } - /// Saves the node to memory. + /// Saves the node to memory and invalidates the cache slot. #[inline] fn save_node(&mut self, node: &mut Node) { node.save(self.allocator_mut()); + self.cache.get_mut().invalidate(node.address()); } /// Replaces the value at `idx` in the node, saves the node, and returns the old value. diff --git a/src/btreemap/node.rs b/src/btreemap/node.rs index e7a4942f..2efb5cd6 100644 --- a/src/btreemap/node.rs +++ b/src/btreemap/node.rs @@ -165,6 +165,21 @@ impl Node { self.get_value(&self.entries[idx], memory) } + /// Reads the value at `idx` without storing it in the node's lazy cache. + /// Avoids inflating cached nodes with large value buffers. + #[inline(always)] + pub fn read_value_uncached(&self, idx: usize, memory: &M) -> Vec { + self.entries[idx] + .1 + .read_uncached(|offset, size| self.load_value_from_memory(offset, size, memory)) + } + + pub fn get_key_read_value_uncached(&self, idx: usize, memory: &M) -> Entry { + let key = self.get_key(&self.entries[idx], memory).clone(); + let value = self.read_value_uncached(idx, memory); + (key, value) + } + /// Extracts the contents of key (by loading it first if it's not loaded yet). fn extract_key(&self, key: LazyKey, memory: &M) -> K { key.take_or_load(|offset, size| self.load_key_from_memory(offset, size, memory)) @@ -278,15 +293,6 @@ impl Node { .insert(idx, (LazyKey::by_value(key), LazyValue::by_value(value))); } - /// Returns the entry at the specified index while consuming this node. - pub fn extract_entry_at(&mut self, idx: usize, memory: &M) -> Entry { - let (key, value) = self.entries.swap_remove(idx); - ( - self.extract_key(key, memory), - self.extract_value(value, memory), - ) - } - /// Removes the entry at the specified index. pub fn remove_entry(&mut self, idx: usize, memory: &M) -> Entry { let (key, value) = self.entries.remove(idx); @@ -562,6 +568,24 @@ impl LazyValue { pub fn take_or_load(self, load: impl FnOnce(Bytes, u32) -> Blob) -> Blob { self.0.take_or_load(load) } + + /// Reads the value without populating the OnceCell. + /// Returns the already-loaded value if present, otherwise reads from memory + /// into a fresh buffer without storing it in the node. + #[inline(always)] + fn read_uncached(&self, load: impl FnOnce(Bytes, u32) -> Blob) -> Blob { + match &self.0 { + LazyObject::ByVal(v) => v.clone(), + LazyObject::ByRef { + offset, + size, + loaded, + } => loaded + .get() + .cloned() + .unwrap_or_else(|| load(*offset, *size)), + } + } } #[derive(Debug)] diff --git a/src/btreemap/node_cache.rs b/src/btreemap/node_cache.rs new file mode 100644 index 00000000..4777bebd --- /dev/null +++ b/src/btreemap/node_cache.rs @@ -0,0 +1,448 @@ +use crate::types::{Address, NULL}; +use crate::Storable; + +use super::node::Node; + +/// Node-cache performance metrics. +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] +pub struct NodeCacheMetrics { + /// Successful cache lookups. + hit_counter: u64, + + /// Misses where the target slot was empty. + cold_miss_counter: u64, + + /// Misses where the slot was occupied by a different node. + collision_miss_counter: u64, +} + +impl NodeCacheMetrics { + pub fn new() -> Self { + Self::default() + } + + /// Resets all counters to zero. + pub fn clear_counters(&mut self) { + self.hit_counter = 0; + self.cold_miss_counter = 0; + self.collision_miss_counter = 0; + } + + /// Returns the number of successful cache lookups. + pub fn hits(&self) -> u64 { + self.hit_counter + } + + /// Returns the number of cache misses where the target slot was empty. + pub fn cold_misses(&self) -> u64 { + self.cold_miss_counter + } + + /// Returns the number of cache misses where the slot was occupied by a different node. + pub fn collision_misses(&self) -> u64 { + self.collision_miss_counter + } + + /// Returns the total number of cache misses. + pub fn misses(&self) -> u64 { + self.cold_miss_counter + self.collision_miss_counter + } + + /// Returns the total number of cache lookups (hits + misses). + pub fn total(&self) -> u64 { + self.hit_counter + self.cold_miss_counter + self.collision_miss_counter + } + + /// Returns the hit ratio as a value between 0.0 and 1.0. + /// Returns 0.0 if no lookups have been performed. + pub fn hit_ratio(&self) -> f64 { + let total = self.total(); + if total == 0 { + 0.0 + } else { + self.hit_counter as f64 / total as f64 + } + } + + fn observe_hit(&mut self) { + self.hit_counter += 1; + } + + fn observe_cold_miss(&mut self) { + self.cold_miss_counter += 1; + } + + fn observe_collision_miss(&mut self) { + self.collision_miss_counter += 1; + } +} + +/// A single slot in the direct-mapped node cache. +struct CacheSlot { + /// The stable-memory address of the cached node. + address: Address, + + /// The cached node, or `None` if the slot is empty. + node: Option>, + + /// Distance from the tree root (root = 0). Used by the eviction policy. + depth: u8, +} + +impl CacheSlot { + fn empty() -> Self { + Self { + address: NULL, + node: None, + depth: 0, + } + } +} + +/// A direct-mapped node cache. +/// +/// Each slot is indexed by `(node_address / page_size) % num_slots`. +/// Collision = eviction (no LRU tracking needed). +/// +/// Nodes at depth 0–1 (root and its immediate children) are pinned: +/// they can evict any node but cannot be evicted by deeper nodes. +/// All other nodes compete on pure recency (last writer wins), so +/// hot leaves and working-set interior nodes can displace stale ones. +pub(super) struct NodeCache { + slots: Vec>, + page_size: u32, + metrics: NodeCacheMetrics, +} + +impl NodeCache { + pub(super) fn new(page_size: u32, num_slots: usize) -> Self { + let mut slots = Vec::with_capacity(num_slots); + for _ in 0..num_slots { + slots.push(CacheSlot::empty()); + } + Self { + slots, + page_size, + metrics: NodeCacheMetrics::new(), + } + } + + pub(super) fn is_enabled(&self) -> bool { + !self.slots.is_empty() + } + + pub(super) fn num_slots(&self) -> usize { + self.slots.len() + } + + pub(super) fn clear(&mut self) { + for slot in &mut self.slots { + *slot = CacheSlot::empty(); + } + self.metrics.clear_counters(); + } + + pub(super) fn slot_size() -> usize { + std::mem::size_of::>() + } + + fn slot_index(&self, addr: Address) -> usize { + debug_assert!(self.is_enabled()); + (addr.get() / self.page_size as u64) as usize % self.slots.len() + } + + pub(super) fn take(&mut self, addr: Address) -> Option> { + if !self.is_enabled() || addr == NULL { + return None; + } + let idx = self.slot_index(addr); + let slot = &mut self.slots[idx]; + if slot.address == addr { + self.metrics.observe_hit(); + slot.address = NULL; + slot.node.take() + } else { + if slot.node.is_none() { + self.metrics.observe_cold_miss(); + } else { + self.metrics.observe_collision_miss(); + } + None + } + } + + pub(super) fn put(&mut self, addr: Address, node: Node, depth: u8) { + if !self.is_enabled() { + return; + } + let idx = self.slot_index(addr); + let slot = &mut self.slots[idx]; + // Always cache into empty slots. Depth 0–1 (root + its children) + // are pinned: a shallower-or-equal pinned node always wins its + // slot, and no unpinned node can displace a pinned one. Among + // unpinned nodes (depth 2+), pure recency applies: any unpinned + // node can evict any other unpinned node, letting hot leaves + // displace stale interior nodes. + let dominated = depth <= slot.depth; + let both_unpinned = depth > 1 && slot.depth > 1; + if slot.node.is_none() || dominated || both_unpinned { + *slot = CacheSlot { + address: addr, + node: Some(node), + depth, + }; + } + } + + pub(super) fn invalidate(&mut self, addr: Address) { + if !self.is_enabled() { + return; + } + let idx = self.slot_index(addr); + let slot = &mut self.slots[idx]; + if slot.address == addr { + *slot = CacheSlot::empty(); + } + } + + pub(super) fn metrics(&self) -> NodeCacheMetrics { + self.metrics + } + + pub(super) fn clear_metrics(&mut self) { + self.metrics.clear_counters(); + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::btreemap::node::{NodeType, PageSize}; + + const PAGE_SIZE: u32 = 64; + const NUM_SLOTS: usize = 4; + + fn make_cache() -> NodeCache> { + NodeCache::new(PAGE_SIZE, NUM_SLOTS) + } + + /// Creates a leaf node at the given address. + fn leaf(addr: u64) -> Node> { + Node::new_v2(Address::from(addr), NodeType::Leaf, PageSize::Value(512)) + } + + /// First realistic node address (past allocator header + chunk header). + const ADDR_A: u64 = 116; + /// Second address that maps to the same cache slot as ADDR_A. + const ADDR_B: u64 = ADDR_A + NUM_SLOTS as u64 * PAGE_SIZE as u64; + + /// Returns a pair of non-NULL addresses that map to the same cache slot. + fn colliding_pair() -> (Address, Address) { + (Address::from(ADDR_A), Address::from(ADDR_B)) + } + + #[test] + fn put_and_take() { + let mut cache = make_cache(); + let addr = Address::from(ADDR_A); + cache.put(addr, leaf(ADDR_A), 0); + assert!(cache.take(addr).is_some()); + } + + #[test] + fn take_removes_entry() { + let mut cache = make_cache(); + let addr = Address::from(ADDR_A); + cache.put(addr, leaf(ADDR_A), 0); + cache.take(addr); + assert!(cache.take(addr).is_none()); + } + + #[test] + fn take_miss_on_empty_slot() { + let mut cache = make_cache(); + assert!(cache.take(Address::from(ADDR_A)).is_none()); + } + + #[test] + fn take_miss_on_wrong_address() { + let mut cache = make_cache(); + let (a, b) = colliding_pair(); + cache.put(a, leaf(ADDR_A), 2); + assert!(cache.take(b).is_none()); + } + + #[test] + fn invalidate_removes_entry() { + let mut cache = make_cache(); + let addr = Address::from(ADDR_A); + cache.put(addr, leaf(ADDR_A), 0); + cache.invalidate(addr); + assert!(cache.take(addr).is_none()); + } + + #[test] + fn invalidate_ignores_wrong_address() { + let mut cache = make_cache(); + let (a, b) = colliding_pair(); + cache.put(a, leaf(ADDR_A), 2); + cache.invalidate(b); // different address, same slot + assert!(cache.take(a).is_some()); + } + + #[test] + fn depth0_cannot_be_evicted_by_depth1() { + let mut cache = make_cache(); + let (a, b) = colliding_pair(); + cache.put(a, leaf(ADDR_A), 0); + cache.put(b, leaf(ADDR_B), 1); + // depth-0 node survives + assert!(cache.take(a).is_some()); + } + + #[test] + fn depth0_cannot_be_evicted_by_depth2() { + let mut cache = make_cache(); + let (a, b) = colliding_pair(); + cache.put(a, leaf(ADDR_A), 0); + cache.put(b, leaf(ADDR_B), 2); + assert!(cache.take(a).is_some()); + } + + #[test] + fn depth1_cannot_be_evicted_by_depth2() { + let mut cache = make_cache(); + let (a, b) = colliding_pair(); + cache.put(a, leaf(ADDR_A), 1); + cache.put(b, leaf(ADDR_B), 2); + assert!(cache.take(a).is_some()); + } + + #[test] + fn depth0_evicts_depth1() { + let mut cache = make_cache(); + let (a, b) = colliding_pair(); + cache.put(a, leaf(ADDR_A), 1); + cache.put(b, leaf(ADDR_B), 0); + // depth-0 replaced depth-1 + assert!(cache.take(b).is_some()); + assert!(cache.take(a).is_none()); + } + + #[test] + fn depth0_evicts_depth2() { + let mut cache = make_cache(); + let (a, b) = colliding_pair(); + cache.put(a, leaf(ADDR_A), 2); + cache.put(b, leaf(ADDR_B), 0); + assert!(cache.take(b).is_some()); + } + + #[test] + fn depth1_evicts_depth2() { + let mut cache = make_cache(); + let (a, b) = colliding_pair(); + cache.put(a, leaf(ADDR_A), 2); + cache.put(b, leaf(ADDR_B), 1); + assert!(cache.take(b).is_some()); + } + + // --------------------------------------------------------------- + // Eviction policy: unpinned levels (depth 2+) compete on recency + // --------------------------------------------------------------- + + #[test] + fn depth3_evicts_depth2() { + let mut cache = make_cache(); + let (a, b) = colliding_pair(); + cache.put(a, leaf(ADDR_A), 2); + cache.put(b, leaf(ADDR_B), 3); + // recency wins — deeper node replaced shallower + assert!(cache.take(b).is_some()); + assert!(cache.take(a).is_none()); + } + + #[test] + fn depth2_evicts_depth5() { + let mut cache = make_cache(); + let (a, b) = colliding_pair(); + cache.put(a, leaf(ADDR_A), 5); + cache.put(b, leaf(ADDR_B), 2); + assert!(cache.take(b).is_some()); + } + + #[test] + fn same_depth_evicts() { + let mut cache = make_cache(); + let (a, b) = colliding_pair(); + cache.put(a, leaf(ADDR_A), 3); + cache.put(b, leaf(ADDR_B), 3); + assert!(cache.take(b).is_some()); + assert!(cache.take(a).is_none()); + } + + // --------------------------------------------------------------- + // Disabled cache + // --------------------------------------------------------------- + + #[test] + fn disabled_cache_returns_none() { + let mut cache: NodeCache> = NodeCache::new(PAGE_SIZE, 0); + assert!(!cache.is_enabled()); + cache.put(Address::from(ADDR_A), leaf(ADDR_A), 0); + assert!(cache.take(Address::from(ADDR_A)).is_none()); + } + + // --------------------------------------------------------------- + // NULL address + // --------------------------------------------------------------- + + #[test] + fn take_null_returns_none() { + let mut cache = make_cache(); + assert!(cache.take(NULL).is_none()); + } + + // --------------------------------------------------------------- + // Metrics + // --------------------------------------------------------------- + + #[test] + fn metrics_hit() { + let mut cache = make_cache(); + let addr = Address::from(ADDR_A); + cache.put(addr, leaf(ADDR_A), 0); + cache.take(addr); + assert_eq!(cache.metrics().hits(), 1); + } + + #[test] + fn metrics_cold_miss() { + let mut cache = make_cache(); + cache.take(Address::from(ADDR_A)); + assert_eq!(cache.metrics().cold_misses(), 1); + assert_eq!(cache.metrics().collision_misses(), 0); + } + + #[test] + fn metrics_collision_miss() { + let mut cache = make_cache(); + let (a, b) = colliding_pair(); + cache.put(a, leaf(ADDR_A), 2); + cache.clear_metrics(); + cache.take(b); // occupied by a, not b + assert_eq!(cache.metrics().collision_misses(), 1); + assert_eq!(cache.metrics().cold_misses(), 0); + } + + #[test] + fn clear_resets_slots_and_metrics() { + let mut cache = make_cache(); + let addr = Address::from(ADDR_A); + cache.put(addr, leaf(ADDR_A), 0); + cache.take(addr); + cache.clear(); + assert_eq!(cache.metrics().total(), 0); + assert!(cache.take(addr).is_none()); + } +} diff --git a/src/btreemap/proptests.rs b/src/btreemap/proptests.rs index 79cf6aac..523c0b4c 100644 --- a/src/btreemap/proptests.rs +++ b/src/btreemap/proptests.rs @@ -69,6 +69,18 @@ fn comprehensive(#[strategy(pvec(operation_strategy(), 100..5_000))] ops: Vec) { + let mem = make_memory(); + let mut btree = BTreeMap::new(mem).with_node_cache(32); + let mut std_btree = StdBTreeMap::new(); + + for op in ops.into_iter() { + execute_operation(&mut std_btree, &mut btree, op); + } +} + // A comprehensive fuzz test that runs until it's explicitly terminated. To run: // // ``` diff --git a/src/lib.rs b/src/lib.rs index 5b5a98fc..ce433676 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,7 +19,7 @@ pub mod vec; pub mod vec_mem; pub mod writer; -pub use btreemap::{BTreeMap, BTreeMap as StableBTreeMap}; +pub use btreemap::{BTreeMap, BTreeMap as StableBTreeMap, NodeCacheMetrics}; pub use btreeset::{BTreeSet, BTreeSet as StableBTreeSet}; pub use file_mem::FileMemory; #[cfg(target_arch = "wasm32")]