dfinity / canister-profiling

Collection of canister performance benchmarks
Apache License 2.0
21 stars 8 forks source link

bump ic-cdk and moc #111

Closed chenyan-dfinity closed 5 months ago

github-actions[bot] commented 6 months ago

Note Diffing the performance result against the published result from main branch. Unchanged benchmarks are omitted.

Map

binary_size generate 1m max mem batch_get 50 batch_put 50 batch_remove 50 upgrade
hashmap 189_929 ($\textcolor{red}{0.17\%}$) 8_184_618_025 ($\textcolor{green}{-2.22\%}$) 56_000_256 ($\textcolor{green}{-9.66\%}$) 342_784 ($\textcolor{green}{-0.62\%}$) 6_462_528_122 ($\textcolor{green}{-1.98\%}$) 368_420 ($\textcolor{green}{-0.76\%}$) 10_728_193_099 ($\textcolor{green}{-2.71\%}$)
triemap 195_472 ($\textcolor{green}{-0.02\%}$) 13_661_315_924 ($\textcolor{green}{-1.40\%}$) 68_228_576 ($\textcolor{green}{-8.07\%}$) 252_649 ($\textcolor{green}{-0.76\%}$) 657_794 ($\textcolor{green}{-0.56\%}$) 648_084 ($\textcolor{green}{-0.42\%}$) 15_499_470_884 ($\textcolor{green}{-2.01\%}$)
rbtree 185_907 ($\textcolor{green}{-0.35\%}$) 7_009_043_570 ($\textcolor{green}{-1.66\%}$) 52_000_464 ($\textcolor{green}{-10.34\%}$) 116_348 ($\textcolor{red}{1.79\%}$) 318_320 ($\textcolor{green}{-0.01\%}$) 330_226 ($\textcolor{red}{0.59\%}$) 6_870_900_152 ($\textcolor{green}{-4.16\%}$)
splay 190_457 ($\textcolor{green}{-0.04\%}$) 13_157_617_583 ($\textcolor{green}{-0.68\%}$) 48_000_400 ($\textcolor{green}{-11.10\%}$) 631_329 ($\textcolor{red}{0.42\%}$) 662_998 ($\textcolor{red}{0.21\%}$) 928_144 ($\textcolor{red}{0.67\%}$) 4_308_925_798 ($\textcolor{green}{-5.67\%}$)
btree 230_321 ($\textcolor{red}{0.20\%}$) 10_223_929_607 ($\textcolor{green}{-0.41\%}$) 25_108_416 ($\textcolor{green}{-19.28\%}$) 357_912 ($\textcolor{red}{1.21\%}$) 485_794 ($\textcolor{red}{0.76\%}$) 539_490 ($\textcolor{red}{1.04\%}$) 2_861_974_825 ($\textcolor{green}{-8.68\%}$)
zhenya_hashmap 188_894 ($\textcolor{green}{-0.21\%}$) 2_360_638_679 ($\textcolor{green}{-8.17\%}$) 16_777_504 ($\textcolor{green}{-26.33\%}$) 58_204 ($\textcolor{green}{-3.31\%}$) 66_552 ($\textcolor{green}{-5.11\%}$) 79_675 ($\textcolor{green}{-3.37\%}$) 3_018_208_083 ($\textcolor{green}{-8.69\%}$)
btreemap_rs 553_671 ($\textcolor{red}{3.03\%}$) 1_792_613_493 ($\textcolor{green}{-0.04\%}$) 27_590_656 75_414 ($\textcolor{red}{0.11\%}$) 125_227 ($\textcolor{red}{0.05\%}$) 86_761 ($\textcolor{red}{0.58\%}$) 3_204_184_669 ($\textcolor{red}{9.10\%}$)
imrc_hashmap_rs 555_693 ($\textcolor{red}{2.36\%}$) 2_584_502_429 ($\textcolor{red}{0.00\%}$) 244_973_568 38_020 ($\textcolor{red}{0.68\%}$) 179_184 ($\textcolor{red}{0.14\%}$) 116_050 ($\textcolor{red}{0.58\%}$) 6_262_892_389 ($\textcolor{red}{8.04\%}$)
hashmap_rs 541_994 ($\textcolor{red}{2.37\%}$) 443_248_419 ($\textcolor{red}{0.91\%}$) 73_138_176 21_565 ($\textcolor{red}{0.30\%}$) 26_650 ($\textcolor{green}{-0.23\%}$) 24_961 ($\textcolor{green}{-0.25\%}$) 1_565_649_003 ($\textcolor{red}{20.56\%}$)

Priority queue

binary_size heapify 1m max mem pop_min 50 put 50 pop_min 50.1 upgrade
heap 166_903 ($\textcolor{green}{-0.35\%}$) 5_554_617_018 ($\textcolor{green}{-2.51\%}$) 24_000_360 ($\textcolor{green}{-19.99\%}$) 621_690 ($\textcolor{red}{0.06\%}$) 227_224 ($\textcolor{green}{-0.63\%}$) 592_588 ($\textcolor{red}{0.07\%}$) 3_189_831_485 ($\textcolor{green}{-3.62\%}$)
heap_rs 533_034 ($\textcolor{red}{1.37\%}$) 139_670_245 ($\textcolor{red}{0.00\%}$) 18_284_544 57_437 ($\textcolor{red}{0.03\%}$) 22_991 ($\textcolor{green}{-0.26\%}$) 57_485 ($\textcolor{green}{-0.10\%}$) 648_953_257 ($\textcolor{red}{27.01\%}$)

Growable array

binary_size generate 5k max mem batch_get 500 batch_put 500 batch_remove 500 upgrade
buffer 173_903 ($\textcolor{green}{-0.00\%}$) 2_601_059 ($\textcolor{red}{1.17\%}$) 65_644 95_506 ($\textcolor{red}{0.02\%}$) 803_474 ($\textcolor{red}{0.38\%}$) 173_506 ($\textcolor{red}{1.77\%}$) 3_091_310 ($\textcolor{red}{0.98\%}$)
vector 171_932 ($\textcolor{green}{-0.18\%}$) 1_952_689 ($\textcolor{red}{1.65\%}$) 24_580 126_130 ($\textcolor{red}{0.01\%}$) 186_485 ($\textcolor{red}{1.69\%}$) 176_123 ($\textcolor{red}{0.08\%}$) 4_675_192 ($\textcolor{green}{-0.43\%}$)
vec_rs 533_368 ($\textcolor{red}{2.40\%}$) 289_403 ($\textcolor{red}{0.13\%}$) 1_376_256 17_283 ($\textcolor{red}{0.19\%}$) 30_525 ($\textcolor{green}{-0.15\%}$) 23_285 ($\textcolor{green}{-0.20\%}$) 3_826_760 ($\textcolor{red}{21.06\%}$)

Stable structures

binary_size generate 50k max mem batch_get 50 batch_put 50 batch_remove 50 upgrade
btreemap_rs 553_671 ($\textcolor{red}{3.03\%}$) 76_164_138 ($\textcolor{green}{-0.05\%}$) 2_555_904 64_972 ($\textcolor{red}{0.13\%}$) 97_154 ($\textcolor{red}{0.11\%}$) 85_766 ($\textcolor{red}{0.58\%}$) 139_625_748 ($\textcolor{red}{10.58\%}$)
btreemap_stable_rs 555_424 ($\textcolor{red}{2.17\%}$) 4_564_073_317 ($\textcolor{red}{0.05\%}$) 2_031_616 2_706_123 ($\textcolor{green}{-0.03\%}$) 5_033_940 ($\textcolor{red}{0.15\%}$) 8_578_143 ($\textcolor{green}{-0.19\%}$) 729_341 ($\textcolor{red}{0.00\%}$)
heap_rs 533_034 ($\textcolor{red}{1.37\%}$) 7_052_097 ($\textcolor{red}{0.01\%}$) 2_293_760 49_946 ($\textcolor{red}{0.04\%}$) 23_239 ($\textcolor{green}{-0.26\%}$) 49_834 ($\textcolor{green}{-0.12\%}$) 33_661_680 ($\textcolor{red}{25.75\%}$)
heap_stable_rs 517_798 ($\textcolor{red}{2.22\%}$) 272_319_598 ($\textcolor{red}{0.28\%}$) 458_752 2_306_666 ($\textcolor{red}{0.51\%}$) 239_309 ($\textcolor{red}{0.30\%}$) 2_289_588 ($\textcolor{red}{0.52\%}$) 729_349 ($\textcolor{red}{0.00\%}$)
vec_rs 533_368 ($\textcolor{red}{2.40\%}$) 3_079_779 ($\textcolor{red}{0.01\%}$) 2_293_760 17_283 ($\textcolor{red}{0.19\%}$) 18_375 ($\textcolor{green}{-0.25\%}$) 17_673 ($\textcolor{green}{-0.26\%}$) 31_322_355 ($\textcolor{red}{26.96\%}$)
vec_stable_rs 515_280 ($\textcolor{red}{2.27\%}$) 63_345_046 ($\textcolor{green}{-0.08\%}$) 458_752 64_434 ($\textcolor{red}{3.11\%}$) 79_730 ($\textcolor{red}{0.06\%}$) 83_628 ($\textcolor{red}{2.44\%}$) 729_352 ($\textcolor{red}{0.00\%}$)

Statistics

SHA-2

binary_size SHA-256 SHA-512 account_id neuron_id
Motoko 193_686 ($\textcolor{green}{-1.43\%}$) 267_743_355 ($\textcolor{green}{-1.94\%}$) 247_834_501 ($\textcolor{green}{-4.62\%}$) 33_636 ($\textcolor{green}{-2.14\%}$) 24_532 ($\textcolor{green}{-1.47\%}$)
Rust 538_677 ($\textcolor{red}{0.24\%}$) 82_788_763 ($\textcolor{red}{0.00\%}$) 56_793_160 ($\textcolor{red}{0.00\%}$) 47_956 ($\textcolor{red}{0.09\%}$) 50_870 ($\textcolor{red}{0.96\%}$)

Certified map

binary_size generate 10k max mem inc witness upgrade
Motoko 243_641 ($\textcolor{green}{-0.63\%}$) 4_666_119_661 ($\textcolor{green}{-2.04\%}$) 3_430_044 553_629 ($\textcolor{green}{-2.03\%}$) 407_936 ($\textcolor{red}{1.46\%}$) 274_434_719 ($\textcolor{red}{0.06\%}$)
Rust 579_504 ($\textcolor{red}{2.42\%}$) 6_409_378_412 ($\textcolor{red}{0.00\%}$) 2_228_224 1_020_591 ($\textcolor{red}{0.06\%}$) 304_778 ($\textcolor{red}{0.29\%}$) 6_025_902_205 ($\textcolor{red}{0.11\%}$)

Statistics

Basic DAO

binary_size init transfer_token submit_proposal vote_proposal upgrade
Motoko 273_938 ($\textcolor{green}{-0.55\%}$) 510_793 ($\textcolor{red}{0.02\%}$) 22_320 ($\textcolor{red}{0.26\%}$) 18_541 ($\textcolor{green}{-0.38\%}$) 19_600 ($\textcolor{green}{-0.18\%}$) 157_928 ($\textcolor{red}{0.21\%}$)
Rust 847_032 ($\textcolor{green}{-0.34\%}$) 627_444 ($\textcolor{red}{4.58\%}$) 100_778 ($\textcolor{red}{1.64\%}$) 131_566 ($\textcolor{red}{6.36\%}$) 139_344 ($\textcolor{red}{1.97\%}$) 1_829_093 ($\textcolor{red}{1.62\%}$)

DIP721 NFT

binary_size init mint_token transfer_token upgrade
Motoko 220_403 ($\textcolor{green}{-0.99\%}$) 481_158 30_204 ($\textcolor{red}{1.32\%}$) 8_764 ($\textcolor{green}{-0.14\%}$) 89_833 ($\textcolor{red}{0.42\%}$)
Rust 877_894 ($\textcolor{red}{1.01\%}$) 243_128 ($\textcolor{red}{2.78\%}$) 386_990 ($\textcolor{red}{5.15\%}$) 90_821 ($\textcolor{green}{-1.22\%}$) 2_088_864 ($\textcolor{red}{4.48\%}$)

Statistics

Heartbeat

binary_size heartbeat
Motoko 137_183 ($\textcolor{green}{-0.52\%}$) 23_432 ($\textcolor{red}{20.10\%}$)
Rust 23_657 ($\textcolor{red}{0.08\%}$) 480 ($\textcolor{green}{-56.83\%}$)

Timer

binary_size setTimer cancelTimer
Motoko 145_619 ($\textcolor{green}{-0.46\%}$) 51_778 ($\textcolor{red}{0.24\%}$) 4_626 ($\textcolor{red}{0.35\%}$)
Rust 497_687 ($\textcolor{red}{2.07\%}$) 68_338 ($\textcolor{red}{0.24\%}$) 11_698 ($\textcolor{red}{4.60\%}$)

Statistics

Garbage Collection

generate 700k max mem batch_get 50 batch_put 50 batch_remove 50
default 1_068_192_695 ($\textcolor{green}{-8.81\%}$) 47_793_792 ($\textcolor{green}{-8.07\%}$) 119 119 119
copying 1_068_192_577 ($\textcolor{green}{-8.81\%}$) 47_793_792 ($\textcolor{green}{-8.07\%}$) 1_067_924_316 ($\textcolor{green}{-8.81\%}$) 1_068_004_203 ($\textcolor{green}{-8.81\%}$) 1_067_925_853 ($\textcolor{green}{-8.81\%}$)
compacting 1_545_586_176 ($\textcolor{green}{-7.57\%}$) 47_793_792 ($\textcolor{green}{-8.07\%}$) 1_192_139_528 ($\textcolor{green}{-7.59\%}$) 1_415_425_189 ($\textcolor{green}{-7.69\%}$) 1_439_317_325 ($\textcolor{green}{-8.00\%}$)
generational 2_304_140_531 ($\textcolor{green}{-8.89\%}$) 47_802_256 ($\textcolor{green}{-8.07\%}$) 882_208_645 ($\textcolor{green}{-11.74\%}$) 1_211_144 ($\textcolor{green}{-1.75\%}$) 1_103_549 ($\textcolor{green}{-0.03\%}$)
incremental 29_503_170 976_097_188 ($\textcolor{green}{-0.99\%}$) 471_911_803 ($\textcolor{green}{-1.27\%}$) 497_465_467 ($\textcolor{red}{0.78\%}$) 1_221_308_722 ($\textcolor{red}{11.81\%}$)

Actor class

binary size put new bucket put existing bucket get
Map 299_217 ($\textcolor{green}{-0.32\%}$) 813_521 ($\textcolor{green}{-0.31\%}$) 16_115 ($\textcolor{red}{0.10\%}$) 16_660 ($\textcolor{red}{0.10\%}$)

Statistics

Publisher & Subscriber

pub_binary_size sub_binary_size subscribe_caller subscribe_callee publish_caller publish_callee
Motoko 161_290 ($\textcolor{green}{-0.40\%}$) 145_741 ($\textcolor{green}{-0.33\%}$) 28_593 11_963 22_854 ($\textcolor{green}{-0.04\%}$) 6_446 ($\textcolor{red}{0.25\%}$)
Rust 534_397 ($\textcolor{red}{2.80\%}$) 572_921 ($\textcolor{red}{0.51\%}$) 69_856 ($\textcolor{red}{1.38\%}$) 43_803 ($\textcolor{red}{2.74\%}$) 94_183 ($\textcolor{red}{2.23\%}$) 53_351 ($\textcolor{red}{2.96\%}$)

Statistics

github-actions[bot] commented 6 months ago

Note The flamegraph link only works after you merge. Unchanged benchmarks are omitted.

Collection libraries

Measure different collection libraries written in both Motoko and Rust. The library names with _rs suffix are written in Rust; the rest are written in Motoko. The _stable and _stable_rs suffix represents that the library directly writes the state to stable memory using Region in Motoko and ic-stable-stuctures in Rust.

We use the same random number generator with fixed seed to ensure that all collections contain the same elements, and the queries are exactly the same. Below we explain the measurements of each column in the table:

💎 Takeaways

Note

  • The Candid interface of the benchmark is minimal, therefore the serialization cost is negligible in this measurement.
  • Due to the instrumentation overhead and cycle limit, we cannot profile computations with very large collections.
  • The upgrade column uses Candid for serializing stable data. In Rust, you may get better cycle cost by using a different serialization format. Another slowdown in Rust is that ic-stable-structures tends to be slower than the region memory in Motoko.
  • Different library has different ways for persisting data during upgrades, there are mainly three categories:
    • Use stable variable directly in Motoko: zhenya_hashmap, btree, vector
    • Expose and serialize external state (share/unshare in Motoko, candid::Encode in Rust): rbtree, heap, btreemap_rs, hashmap_rs, heap_rs, vector_rs
    • Use pre/post-upgrade hooks to convert data into an array: hashmap, splay, triemap, buffer, imrc_hashmap_rs
  • The stable benchmarks are much more expensive than their non-stable counterpart, because the stable memory API is much more expensive. The benefit is that they get fast upgrade. The upgrade still needs to parse the metadata when initializing the upgraded Wasm module.
  • hashmap uses amortized data structure. When the initial capacity is reached, it has to copy the whole array, thus the cost of batch_put 50 is much higher than other data structures.
  • btree comes from mops.one/stableheapbtreemap.
  • zhenya_hashmap comes from mops.one/map.
  • vector comes from mops.one/vector. Compare with buffer, put has better worst case time and space complexity ($O(\sqrt{n})$ vs $O(n)$); get has a slightly larger constant overhead.
  • hashmap_rs uses the fxhash crate, which is the same as std::collections::HashMap, but with a deterministic hasher. This ensures reproducible result.
  • imrc_hashmap_rs uses the im-rc crate, which is the immutable version hashmap in Rust.

Map

binary_size generate 1m max mem batch_get 50 batch_put 50 batch_remove 50 upgrade
hashmap 189_929 8_184_618_025 56_000_256 342_784 6_462_528_122 368_420 10_728_193_099
triemap 195_472 13_661_315_924 68_228_576 252_649 657_794 648_084 15_499_470_884
rbtree 185_907 7_009_043_570 52_000_464 116_348 318_320 330_226 6_870_900_152
splay 190_457 13_157_617_583 48_000_400 631_329 662_998 928_144 4_308_925_798
btree 230_321 10_223_929_607 25_108_416 357_912 485_794 539_490 2_861_974_825
zhenya_hashmap 188_894 2_360_638_679 16_777_504 58_204 66_552 79_675 3_018_208_083
btreemap_rs 553_671 1_792_613_493 27_590_656 75_414 125_227 86_761 3_204_184_669
imrc_hashmap_rs 555_693 2_584_502_429 244_973_568 38_020 179_184 116_050 6_262_892_389
hashmap_rs 541_994 443_248_419 73_138_176 21_565 26_650 24_961 1_565_649_003

Priority queue

binary_size heapify 1m max mem pop_min 50 put 50 pop_min 50 upgrade
heap 166_903 5_554_617_018 24_000_360 621_690 227_224 592_588 3_189_831_485
heap_rs 533_034 139_670_245 18_284_544 57_437 22_991 57_485 648_953_257

Growable array

binary_size generate 5k max mem batch_get 500 batch_put 500 batch_remove 500 upgrade
buffer 173_903 2_601_059 65_644 95_506 803_474 173_506 3_091_310
vector 171_932 1_952_689 24_580 126_130 186_485 176_123 4_675_192
vec_rs 533_368 289_403 1_376_256 17_283 30_525 23_285 3_826_760

Stable structures

binary_size generate 50k max mem batch_get 50 batch_put 50 batch_remove 50 upgrade
btreemap_rs 553_671 76_164_138 2_555_904 64_972 97_154 85_766 139_625_748
btreemap_stable_rs 555_424 4_564_073_317 2_031_616 2_706_123 5_033_940 8_578_143 729_341
heap_rs 533_034 7_052_097 2_293_760 49_946 23_239 49_834 33_661_680
heap_stable_rs 517_798 272_319_598 458_752 2_306_666 239_309 2_289_588 729_349
vec_rs 533_368 3_079_779 2_293_760 17_283 18_375 17_673 31_322_355
vec_stable_rs 515_280 63_345_046 458_752 64_434 79_730 83_628 729_352

Environment

  • dfx 0.18.0
  • Motoko compiler 0.11.0 (source lndfxrzc-zr7pf1k6-nr3nr3d7-jfla8nbn)
  • rustc 1.76.0 (07dca489a 2024-02-04)
  • ic-repl 0.7.0
  • ic-wasm 0.7.0

    Cryptographic libraries

Measure different cryptographic libraries written in both Motoko and Rust.

SHA-2

binary_size SHA-256 SHA-512 account_id neuron_id
Motoko 193_686 267_743_355 247_834_501 33_636 24_532
Rust 538_677 82_788_763 56_793_160 47_956 50_870

Certified map

binary_size generate 10k max mem inc witness upgrade
Motoko 243_641 4_666_119_661 3_430_044 553_629 407_936 274_434_719
Rust 579_504 6_409_378_412 2_228_224 1_020_591 304_778 6_025_902_205

Environment

  • dfx 0.18.0
  • Motoko compiler 0.11.0 (source lndfxrzc-zr7pf1k6-nr3nr3d7-jfla8nbn)
  • rustc 1.76.0 (07dca489a 2024-02-04)
  • ic-repl 0.7.0
  • ic-wasm 0.7.0

    Sample Dapps

Measure the performance of some typical dapps:

Note

  • The cost difference is mainly due to the Candid serialization cost.
  • Motoko statically compiles/specializes the serialization code for each method, whereas in Rust, we use serde to dynamically deserialize data based on data on the wire.
  • We could improve the performance on the Rust side by using parser combinators. But it is a challenge to maintain the ergonomics provided by serde.
  • For real-world applications, we tend to send small data for each endpoint, which makes the Candid overhead in Rust tolerable.

Basic DAO

binary_size init transfer_token submit_proposal vote_proposal upgrade
Motoko 273_938 510_793 22_320 18_541 19_600 157_928
Rust 847_032 627_444 100_778 131_566 139_344 1_829_093

DIP721 NFT

binary_size init mint_token transfer_token upgrade
Motoko 220_403 481_158 30_204 8_764 89_833
Rust 877_894 243_128 386_990 90_821 2_088_864

Environment

  • dfx 0.18.0
  • Motoko compiler 0.11.0 (source lndfxrzc-zr7pf1k6-nr3nr3d7-jfla8nbn)
  • rustc 1.76.0 (07dca489a 2024-02-04)
  • ic-repl 0.7.0
  • ic-wasm 0.7.0

    Heartbeat / Timer

Measure the cost of empty heartbeat and timer job.

Heartbeat

binary_size heartbeat
Motoko 137_183 23_432
Rust 23_657 480

Timer

binary_size setTimer cancelTimer
Motoko 145_619 51_778 4_626
Rust 497_687 68_338 11_698

Environment

  • dfx 0.18.0
  • Motoko compiler 0.11.0 (source lndfxrzc-zr7pf1k6-nr3nr3d7-jfla8nbn)
  • rustc 1.76.0 (07dca489a 2024-02-04)
  • ic-repl 0.7.0
  • ic-wasm 0.7.0

    Motoko Specific Benchmarks

Measure various features only available in Motoko.

Garbage Collection

generate 700k max mem batch_get 50 batch_put 50 batch_remove 50
default 1_068_192_695 47_793_792 119 119 119
copying 1_068_192_577 47_793_792 1_067_924_316 1_068_004_203 1_067_925_853
compacting 1_545_586_176 47_793_792 1_192_139_528 1_415_425_189 1_439_317_325
generational 2_304_140_531 47_802_256 882_208_645 1_211_144 1_103_549
incremental 29_503_170 976_097_188 471_911_803 497_465_467 1_221_308_722

Actor class

binary size put new bucket put existing bucket get
Map 299_217 813_521 16_115 16_660

Environment

  • dfx 0.18.0
  • Motoko compiler 0.11.0 (source lndfxrzc-zr7pf1k6-nr3nr3d7-jfla8nbn)
  • rustc 1.76.0 (07dca489a 2024-02-04)
  • ic-repl 0.7.0
  • ic-wasm 0.7.0

    Publisher & Subscriber

Measure the cost of inter-canister calls from the Publisher & Subscriber example.

pub_binary_size sub_binary_size subscribe_caller subscribe_callee publish_caller publish_callee
Motoko 161_290 145_741 28_593 11_963 22_854 6_446
Rust 534_397 572_921 69_856 43_803 94_183 53_351

Environment

  • dfx 0.18.0
  • Motoko compiler 0.11.0 (source lndfxrzc-zr7pf1k6-nr3nr3d7-jfla8nbn)
  • rustc 1.76.0 (07dca489a 2024-02-04)
  • ic-repl 0.7.0
  • ic-wasm 0.7.0