diff --git a/.github/workflows/diff.py b/.github/workflows/diff.py index f021d577..ae3408e9 100644 --- a/.github/workflows/diff.py +++ b/.github/workflows/diff.py @@ -76,7 +76,10 @@ def read_tables(file): for col in current.columns: x = row[col] base = main.loc[idx, col] - d = (x - base) / base * 100 + if base == 0: + d = 0 + else: + d = (x - base) / base * 100 if d < 0: result.loc[idx, col] = f"{x:_} ($\\textcolor{{green}}{{{d:.2f}\\\\%}}$)" elif d > 0: diff --git a/.github/workflows/perf.yml b/.github/workflows/perf.yml index c4bd3b20..dafd32d5 100644 --- a/.github/workflows/perf.yml +++ b/.github/workflows/perf.yml @@ -7,15 +7,16 @@ on: jobs: perf: - runs-on: ubuntu-latest + runs-on: macos-latest strategy: fail-fast: false env: - DFX_VERSION: 0.14.3 - IC_REPL_VERSION: 0.4.1 - MOC_VERSION: 0.9.7 - IC_WASM_VERSION: 0.4.0 - MOC_ARTIFACT: 1242499691 + DFX_VERSION: 0.16.1 + IC_REPL_VERSION: 0.6.2 + MOC_VERSION: 0.10.4 + IC_WASM_VERSION: 0.7.0 + RUSTC_VERSION: 1.75.0 + MOC_ARTIFACT: 1242507845 steps: - uses: actions/checkout@v3 - name: Checkout out gh-pages report @@ -33,7 +34,7 @@ jobs: - uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: stable + toolchain: ${{ env.RUSTC_VERSION }} override: true target: wasm32-unknown-unknown - name: Cache cargo build @@ -43,7 +44,7 @@ jobs: ~/.cargo/registry ~/.cargo/git target - key: cargo-${{ hashFiles('**/Cargo.lock') }} + key: cargo-${{ env.RUSTC_VERSION }}-${{ hashFiles('**/Cargo.lock') }} - uses: actions/setup-python@v4 if: github.event_name == 'pull_request' with: @@ -59,8 +60,8 @@ jobs: - name: Install ic-repl, mops, dfx, and moc run: | echo y | DFX_VERSION=$DFX_VERSION bash -ci "$(curl -fsSL https://smartcontracts.org/install.sh)" - wget https://github.com/chenyan2002/ic-repl/releases/download/$IC_REPL_VERSION/ic-repl-linux64 - cp ./ic-repl-linux64 /usr/local/bin/ic-repl + wget https://github.com/chenyan2002/ic-repl/releases/download/$IC_REPL_VERSION/ic-repl-macos + cp ./ic-repl-macos /usr/local/bin/ic-repl chmod a+x /usr/local/bin/ic-repl npm i -g ic-mops dfx cache install @@ -70,6 +71,11 @@ jobs: cp -rf bin/moc $(dfx cache show) wget https://github.com/dfinity/ic-wasm/releases/download/$IC_WASM_VERSION/ic-wasm-linux64 cp ./ic-wasm-linux64 /usr/local/bin/ic-wasm + cd $(dfx cache show) + wget https://github.com/dfinity/motoko/releases/download/$MOC_VERSION/motoko-Darwin-x86_64-$MOC_VERSION.tar.gz + tar zxvf motoko-Darwin-x86_64-$MOC_VERSION.tar.gz + wget https://github.com/dfinity/ic-wasm/releases/download/$IC_WASM_VERSION/ic-wasm-macos + cp ./ic-wasm-macos /usr/local/bin/ic-wasm chmod a+x /usr/local/bin/ic-wasm - name: Setup system subnet and start dfx run: | @@ -84,7 +90,9 @@ jobs: dfx stop dfx start --clean --background - name: Run perf - run: make + run: | + make + make emit_version - name: Generate table if: github.event_name == 'pull_request' env: diff --git a/Cargo.lock b/Cargo.lock index 49ba5c7e..f9b88e00 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "anyhow" -version = "1.0.69" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800" +checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" [[package]] name = "arrayvec" @@ -27,6 +27,7 @@ dependencies = [ "candid", "ic-cdk", "serde", + "utils", ] [[package]] @@ -76,58 +77,68 @@ version = "0.1.0" dependencies = [ "candid", "ic-cdk", + "utils", +] + +[[package]] +name = "btreemap_stable" +version = "0.1.0" +dependencies = [ + "candid", + "ic-cdk", + "ic-stable-structures", + "utils", ] [[package]] name = "byteorder" -version = "1.4.3" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "candid" -version = "0.9.1" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4df671c37a9c6168db0334f2b289dd4e02dea1bbefe1fb22c5d43b12d865aacd" +checksum = "182543fbc03b4ad0bfc384e6b68346e0b0aad0b11d075b71b4fcaa5d07f8862c" dependencies = [ "anyhow", "binread", "byteorder", "candid_derive", - "codespan-reporting", - "crc32fast", - "data-encoding", "hex", + "ic_principal", "leb128", "num-bigint", "num-traits", - "num_enum", "paste", "pretty", "serde", "serde_bytes", - "sha2", "stacker", "thiserror", ] [[package]] name = "candid_derive" -version = "0.6.2" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "810b3bd60244f282090652ffc7c30a9d23892e72dfe443e46ee55569044f7dd5" +checksum = "970c220da8aa2fa6f7ef5dbbf3ea5b620a59eb3ac107cfb95ae8c6eebdfb7a08" dependencies = [ "lazy_static", "proc-macro2", "quote", - "syn 2.0.12", + "syn 2.0.48", ] [[package]] name = "cc" -version = "1.0.79" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] [[package]] name = "certified_map" @@ -139,6 +150,7 @@ dependencies = [ "serde", "serde_bytes", "serde_cbor", + "utils", ] [[package]] @@ -147,30 +159,20 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "codespan-reporting" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" -dependencies = [ - "termcolor", - "unicode-width", -] - [[package]] name = "cpufeatures" -version = "0.2.5" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" dependencies = [ "libc", ] [[package]] name = "crc32fast" -version = "1.3.2" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" dependencies = [ "cfg-if", ] @@ -187,15 +189,15 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" +checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" [[package]] name = "digest" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", @@ -208,19 +210,20 @@ dependencies = [ "candid", "ic-cdk", "serde", + "utils", ] [[package]] name = "either" -version = "1.8.1" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" [[package]] name = "futures" -version = "0.3.27" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "531ac96c6ff5fd7c62263c5e3c67a603af4fcaee2e1a0ae5565ba3a11e69e549" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" dependencies = [ "futures-channel", "futures-core", @@ -233,9 +236,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.27" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "164713a5a0dcc3e7b4b1ed7d3b433cabc18025386f9339346e8daf15963cf7ac" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", "futures-sink", @@ -243,15 +246,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.27" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86d7a0c1aa76363dac491de0ee99faf6941128376f1cf96f07db7603b7de69dd" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-executor" -version = "0.3.27" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1997dd9df74cdac935c76252744c1ed5794fac083242ea4fe77ef3ed60ba0f83" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" dependencies = [ "futures-core", "futures-task", @@ -260,38 +263,38 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.27" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89d422fa3cbe3b40dca574ab087abb5bc98258ea57eea3fd6f1fa7162c778b91" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" [[package]] name = "futures-macro" -version = "0.3.27" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3eb14ed937631bd8b8b8977f2c198443447a8355b6e3ca599f38c975e5a963b6" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.48", ] [[package]] name = "futures-sink" -version = "0.3.27" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec93083a4aecafb2a80a885c9de1f0ccae9dbd32c2bb54b0c3a65690e0b8d2f2" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" [[package]] name = "futures-task" -version = "0.3.27" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd65540d33b37b16542a0438c12e6aeead10d4ac5d05bd3f805b8f35ab592879" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-util" -version = "0.3.27" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ef6b17e481503ec85211fed8f39d1970f128935ca1f814cd32ac4a6842e84ab" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-channel", "futures-core", @@ -316,9 +319,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.6" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", @@ -330,12 +333,6 @@ version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - [[package]] name = "hashmap" version = "0.1.0" @@ -343,6 +340,7 @@ dependencies = [ "candid", "fxhash", "ic-cdk", + "utils", ] [[package]] @@ -351,6 +349,17 @@ version = "0.1.0" dependencies = [ "candid", "ic-cdk", + "utils", +] + +[[package]] +name = "heap_stable" +version = "0.1.0" +dependencies = [ + "candid", + "ic-cdk", + "ic-stable-structures", + "utils", ] [[package]] @@ -370,9 +379,9 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "ic-cdk" -version = "0.10.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d4c0b932bf454d5d60e61e13c3c944972fcfd74dc82b9ed5c8b0a75979cf50" +checksum = "9f3d204af0b11c45715169c997858edb58fa8407d08f4fae78a6b415dd39a362" dependencies = [ "candid", "ic-cdk-macros", @@ -383,9 +392,9 @@ dependencies = [ [[package]] name = "ic-cdk-macros" -version = "0.7.0" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4587624e64b8db56224033ee74e5c246d39be15375d03d3df7c117d49d18487" +checksum = "a5a618e4020cea88e933d8d2f8c7f86d570ec06213506a80d4f2c520a9bba512" dependencies = [ "candid", "proc-macro2", @@ -397,9 +406,9 @@ dependencies = [ [[package]] name = "ic-cdk-timers" -version = "0.4.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "198e55e4d9e069903fbea1dceae6fd28f7e0b38d5a4e1026ed2c772e1d55f5e0" +checksum = "8c43b9706fef3ad10c4192a14801d16bd9539068239f0f06f257857441364329" dependencies = [ "futures", "ic-cdk", @@ -420,11 +429,33 @@ dependencies = [ "sha2", ] +[[package]] +name = "ic-stable-structures" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "774d7d26420c095f2b5f0f71f7b2ff4a5b58b87e0959dccc78b3d513a7db5112" +dependencies = [ + "ic_principal", +] + [[package]] name = "ic0" -version = "0.18.11" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "576c539151d4769fb4d1a0c25c4108dd18facd04c5695b02cf2d226ab4e43aa5" +checksum = "a54b5297861c651551676e8c43df805dad175cc33bc97dbd992edbbb85dcbcdf" + +[[package]] +name = "ic_principal" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1762deb6f7c8d8c2bdee4b6c5a47b60195b74e9b5280faa5ba29692f8e17429c" +dependencies = [ + "crc32fast", + "data-encoding", + "serde", + "sha2", + "thiserror", +] [[package]] name = "im-rc" @@ -447,16 +478,7 @@ dependencies = [ "candid", "ic-cdk", "im-rc", -] - -[[package]] -name = "indexmap" -version = "1.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" -dependencies = [ - "autocfg", - "hashbrown", + "utils", ] [[package]] @@ -473,21 +495,21 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" [[package]] name = "libc" -version = "0.2.140" +version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "memchr" -version = "2.5.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" [[package]] name = "num-bigint" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" dependencies = [ "autocfg", "num-integer", @@ -497,61 +519,33 @@ dependencies = [ [[package]] name = "num-integer" -version = "0.1.45" +version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" dependencies = [ - "autocfg", "num-traits", ] [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" dependencies = [ "autocfg", ] -[[package]] -name = "num_enum" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a015b430d3c108a207fd776d2e2196aaf8b1cf8cf93253e3a097ff3085076a1" -dependencies = [ - "num_enum_derive", -] - -[[package]] -name = "num_enum_derive" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 2.0.12", -] - -[[package]] -name = "once_cell" -version = "1.17.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" - [[package]] name = "paste" -version = "1.0.12" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" [[package]] name = "pin-project-lite" -version = "0.2.9" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" [[package]] name = "pin-utils" @@ -561,30 +555,20 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pretty" -version = "0.12.1" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "563c9d701c3a31dfffaaf9ce23507ba09cbe0b9125ba176d15e629b0235e9acc" +checksum = "b55c4d17d994b637e2f4daf6e5dc5d660d209d5642377d675d7a1c3ab69fa579" dependencies = [ "arrayvec", "typed-arena", - "unicode-segmentation", -] - -[[package]] -name = "proc-macro-crate" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" -dependencies = [ - "once_cell", - "toml_edit", + "unicode-width", ] [[package]] name = "proc-macro2" -version = "1.0.52" +version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d0e1ae9e836cc3beddd63db0df682593d7e2d3d891ae8c9083d2113e1744224" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" dependencies = [ "unicode-ident", ] @@ -609,9 +593,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.26" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] @@ -633,24 +617,24 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.12" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" [[package]] name = "serde" -version = "1.0.155" +version = "1.0.196" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71f2b4817415c6d4210bfe1c7bfcf4801b2d904cb4d0e1a8fdb651013c9e86b8" +checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32" dependencies = [ "serde_derive", ] [[package]] name = "serde_bytes" -version = "0.11.9" +version = "0.11.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "416bda436f9aab92e02c8e10d49a15ddd339cea90b6e340fe51ed97abb548294" +checksum = "8b8497c313fd43ab992087548117643f6fcd935cbf36f176ffda0aacf9591734" dependencies = [ "serde", ] @@ -667,13 +651,13 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.155" +version = "1.0.196" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d071a94a3fac4aff69d023a7f411e33f40f3483f8c5190b1953822b6b76d7630" +checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.48", ] [[package]] @@ -699,9 +683,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.6" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ "cfg-if", "cpufeatures", @@ -720,18 +704,18 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ "autocfg", ] [[package]] name = "slotmap" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1e08e261d0e8f5c43123b7adf3e4ca1690d655377ac93a03b2c9d3e98de1342" +checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a" dependencies = [ "version_check", ] @@ -771,42 +755,33 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.12" +version = "2.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79d9531f94112cfc3e4c8f5f02cb2b58f72c97b7efd85f70203cc6d8efda5927" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] -[[package]] -name = "termcolor" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" -dependencies = [ - "winapi-util", -] - [[package]] name = "thiserror" -version = "1.0.39" +version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5ab016db510546d856297882807df8da66a16fb8c4101cb8b30054b0d5b2d9c" +checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.39" +version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5420d42e90af0c38c3290abcca25b9b3bdf379fc9f55c528f53a269d9c9a267e" +checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.48", ] [[package]] @@ -818,23 +793,6 @@ dependencies = [ "ic-cdk-timers", ] -[[package]] -name = "toml_datetime" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622" - -[[package]] -name = "toml_edit" -version = "0.19.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08de71aa0d6e348f070457f85af8bd566e2bc452156a423ddf22861b3a953fae" -dependencies = [ - "indexmap", - "toml_datetime", - "winnow", -] - [[package]] name = "typed-arena" version = "2.0.2" @@ -843,27 +801,29 @@ checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" [[package]] name = "typenum" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "unicode-ident" -version = "1.0.8" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] -name = "unicode-segmentation" -version = "1.10.1" +name = "unicode-width" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" +checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" [[package]] -name = "unicode-width" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" +name = "utils" +version = "0.1.0" +dependencies = [ + "candid", + "ic-stable-structures", +] [[package]] name = "vector" @@ -871,6 +831,17 @@ version = "0.1.0" dependencies = [ "candid", "ic-cdk", + "utils", +] + +[[package]] +name = "vector_stable" +version = "0.1.0" +dependencies = [ + "candid", + "ic-cdk", + "ic-stable-structures", + "utils", ] [[package]] @@ -895,26 +866,8 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -[[package]] -name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "winnow" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee7b2c67f962bf5042bfd8b6a916178df33a26eec343ae064cb8e069f638fa6f" -dependencies = [ - "memchr", -] diff --git a/Cargo.toml b/Cargo.toml index 821c72ab..ee3660a6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,10 +1,14 @@ [workspace] members = [ + "utils/rust", "collections/rust/src/hashmap", "collections/rust/src/btreemap", + "collections/rust/src/btreemap_stable", "collections/rust/src/heap", + "collections/rust/src/heap_stable", "collections/rust/src/imrc_hashmap", "collections/rust/src/vector", + "collections/rust/src/vector_stable", "crypto/rust/src/sha", "crypto/rust/src/certified_map", "dapps/rust/dip721-nft", @@ -14,6 +18,7 @@ members = [ "pub-sub/rust/publisher", "pub-sub/rust/subscriber", ] +resolver = "2" [profile.release] panic = "abort" @@ -21,7 +26,8 @@ lto = true opt-level = 2 [workspace.dependencies] -ic-cdk = "0.10.0" -ic-cdk-timers = "0.4.0" -candid = "0.9" +ic-cdk = "0.12.0" +ic-cdk-timers = "0.6.0" +candid = "0.10.0" serde = "1" +ic-stable-structures = "0.6" diff --git a/Makefile b/Makefile index a912aa69..6e335a94 100644 --- a/Makefile +++ b/Makefile @@ -3,3 +3,12 @@ APPS = dapps collections crypto pub-sub heartbeat motoko all: $(foreach test_dir,$(APPS),make -C $(test_dir) &&) true +emit_version: + for f in _out/*/README.md; do \ + echo "\n> ## Environment" >> $$f; \ + (printf "> * "; dfx --version) >> $$f; \ + (printf "> * "; $$(dfx cache show)/moc --version) >> $$f; \ + (printf "> * "; rustc --version) >> $$f; \ + (printf "> * "; ic-repl --version) >> $$f; \ + (printf "> * "; ic-wasm --version) >> $$f; \ + done diff --git a/README.md b/README.md index 2568e75f..968cdfa3 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,24 @@ Performance reports are generated in `gh-pages` branch. The reported Wasm binary ## How to reproduce performance report +### Prerequisites: + +- [Install Internet Computer SDK](https://sdk.dfinity.org/docs/quickstart/local-quickstart.html) +- [Install ic-wasm](https://github.com/dfinity/ic-wasm/releases) +- [Install ic-repl](https://github.com/dfinity/ic-repl/releases) +- [Install npm](https://nodejs.org/en/download/) +- [Install mops](https://mops.one/docs/install) + ``` + npm i -g ic-mops + ``` +- [Install Rust](https://www.rust-lang.org/tools/install) +- Add wasm32 target to Rust + ``` + rustup target add wasm32-unknown-unknown + ``` + +### Running locally: + * Make sure that local replica is configured as system subnet. If not, run `cp networks.json ~/.config/dfx/` * `dfx start --clean` * Run `make -e MOC_VERSION=` @@ -53,7 +71,7 @@ Benchmark_name/ ## Wasm Optimizer -A Wasm optimizer is applied to each Wasm binary before instrumentation (except the GC benchmarks). The optimizer can be found in [ic-wasm](https://github.com/dfinity/ic-wasm), which wraps [wasm-opt](https://github.com/WebAssembly/binaryen). +A Wasm optimizer is applied to each Wasm binary before instrumentation. The optimizer can be found in [ic-wasm](https://github.com/dfinity/ic-wasm), which wraps [wasm-opt](https://github.com/WebAssembly/binaryen). The following optimizations are applied: ``` diff --git a/collections/Makefile b/collections/Makefile index 17c3ce4b..8222603c 100644 --- a/collections/Makefile +++ b/collections/Makefile @@ -1,36 +1,15 @@ -APP = collections +include ../*.mk .PHONY: all motoko rust build perf all: build perf motoko: - set -e; \ - cd motoko; \ - envsubst < mops.template.toml > mops.toml; \ - mops install; \ - dfx canister create --all; \ - dfx ledger fabricate-cycles --t 100 --canister $$(dfx identity get-wallet); \ - dfx build; \ - echo "Optimize with ic-wasm level 3"; \ - for f in .dfx/local/canisters/*/*.wasm; do ic-wasm -o $$f $$f shrink --optimize O3 --keep-name-section; done; \ - rm mops.toml; \ - cd .. + $(call build_with_mops,motoko) rust: - set -e; \ - cd rust; \ - dfx canister create --all; \ - dfx ledger fabricate-cycles --t 100 --canister $$(dfx identity get-wallet); \ - dfx build; \ - echo "Optimize with ic-wasm level 3"; \ - for f in .dfx/local/canisters/*/*.wasm; do ic-wasm -o $$f $$f shrink --optimize O3 --keep-name-section; done; \ - cd .. + $(call build,rust) build: motoko rust perf: - set -e; \ - mkdir -p ../_out/$(APP); \ - cp README.md ../_out/$(APP); \ - cd ../_out/$(APP); \ - ic-repl ../../$(APP)/perf.sh + $(call perf,collections,perf.sh) diff --git a/collections/README.md b/collections/README.md index ee95f38a..32269267 100644 --- a/collections/README.md +++ b/collections/README.md @@ -2,6 +2,7 @@ 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: @@ -11,6 +12,7 @@ the same elements, and the queries are exactly the same. Below we explain the me * batch_get 50. Find 50 elements from the collection. * batch_put 50. Insert 50 elements to the collection. * batch_remove 50. Remove 50 elements from the collection. +* upgrade. Upgrade the canister with the same Wasm module. For non-stable benchmarks, the map state is persisted by serializing and deserializing states into stable memory. For stable benchmarks, the upgrade takes no cycles, as the state is already in the stable memory. ## **💎 Takeaways** @@ -22,7 +24,13 @@ the same elements, and the queries are exactly the same. Below we explain the me > **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 large collections. Hopefully, when deterministic time slicing is ready, we can measure the performance on larger memory footprint. +> * 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](https://mops.one/stableheapbtreemap). > * `zhenya_hashmap` comes from [mops.one/map](https://mops.one/map). diff --git a/collections/motoko/mops.template.toml b/collections/motoko/mops.template.toml index 38a116a7..7d80002f 100644 --- a/collections/motoko/mops.template.toml +++ b/collections/motoko/mops.template.toml @@ -1,6 +1,6 @@ [dependencies] base = "https://github.com/dfinity/motoko-base#moc-$MOC_VERSION" stableheapbtreemap = "1.3.0" -map = "8.1.0" +map = "9.0.0" splay = "0.1.0" -vector = "0.1.2" +vector = "0.2.0" diff --git a/collections/motoko/src/ZhenyaHashmap.mo b/collections/motoko/src/ZhenyaHashmap.mo index 426625ad..7d2d20e9 100644 --- a/collections/motoko/src/ZhenyaHashmap.mo +++ b/collections/motoko/src/ZhenyaHashmap.mo @@ -4,11 +4,14 @@ import Hash "mo:base/Hash"; import Iter "mo:base/Iter"; import Option "mo:base/Option"; import Random "random"; +import Profiling "../../../utils/motoko/Profiling"; actor { + stable let profiling = Profiling.init(); + func f_hash(x : Nat64) : Nat32 = Hash.hash(Nat64.toNat x); - let hash : HashMap.HashUtils = (f_hash, Nat64.equal, func() = 0); - stable var map = HashMap.new(hash); + let hash : HashMap.HashUtils = (f_hash, Nat64.equal); + stable var map = HashMap.new(); let rand = Random.new(null, 42); public func generate(size : Nat32) : async () { diff --git a/collections/motoko/src/btreemap.mo b/collections/motoko/src/btreemap.mo index f11af7eb..9709ed03 100644 --- a/collections/motoko/src/btreemap.mo +++ b/collections/motoko/src/btreemap.mo @@ -3,8 +3,11 @@ import Nat64 "mo:base/Nat64"; import Iter "mo:base/Iter"; import Option "mo:base/Option"; import Random "random"; +import Profiling "../../../utils/motoko/Profiling"; actor { + stable let profiling = Profiling.init(); + stable var map = Map.init(null); let rand = Random.new(null, 42); diff --git a/collections/motoko/src/buffer.mo b/collections/motoko/src/buffer.mo index 200ccec6..81e869df 100644 --- a/collections/motoko/src/buffer.mo +++ b/collections/motoko/src/buffer.mo @@ -3,9 +3,20 @@ import Iter "mo:base/Iter"; import Random "random"; import Nat64 "mo:base/Nat64"; import Option "mo:base/Option"; +import Profiling "../../../utils/motoko/Profiling"; actor { + stable let profiling = Profiling.init(); + var buffer = Buffer.Buffer(0); + stable var stableBuffer : [Nat64] = []; + + system func preupgrade() { + stableBuffer := Buffer.toArray(buffer); + }; + system func postupgrade() { + buffer := Buffer.fromArray(stableBuffer); + }; public func generate(n: Nat) : async () { buffer.clear(); diff --git a/collections/motoko/src/hashmap.mo b/collections/motoko/src/hashmap.mo index 583830ab..92b70983 100644 --- a/collections/motoko/src/hashmap.mo +++ b/collections/motoko/src/hashmap.mo @@ -5,12 +5,23 @@ import Hash "mo:base/Hash"; import Iter "mo:base/Iter"; import Option "mo:base/Option"; import Random "random"; +import Profiling "../../../utils/motoko/Profiling"; actor { + stable let profiling = Profiling.init(); + func hash(x: Nat64) : Nat32 = Hash.hash(Nat64.toNat x); var map = HashMap.HashMap(0, Nat64.equal, hash); + stable var stableMap: [(Nat64, Nat64)] = []; let rand = Random.new(null, 42); + system func preupgrade() { + stableMap := Iter.toArray(map.entries()) + }; + system func postupgrade() { + map := HashMap.fromIter(stableMap.vals(), stableMap.size(), Nat64.equal, hash); + }; + public func generate(size: Nat32) : async () { let rand = Random.new(?size, 1); let iter = Iter.map(rand, func x = (x, x)); diff --git a/collections/motoko/src/heap.mo b/collections/motoko/src/heap.mo index 5d45aa28..129fa9ae 100644 --- a/collections/motoko/src/heap.mo +++ b/collections/motoko/src/heap.mo @@ -4,10 +4,21 @@ import Iter "mo:base/Iter"; import Option "mo:base/Option"; import Random "random"; import O "mo:base/Order"; +import Profiling "../../../utils/motoko/Profiling"; actor { + stable let profiling = Profiling.init(); + var map = Heap.Heap(Nat64.compare); + stable var stableMap : Heap.Tree = null; let rand = Random.new(null, 42); + + system func preupgrade() { + stableMap := map.share(); + }; + system func postupgrade() { + map.unsafeUnshare(stableMap); + }; public func generate(size: Nat32) : async () { let rand = Random.new(?size, 1); diff --git a/collections/motoko/src/rbtree.mo b/collections/motoko/src/rbtree.mo index 50ab5d50..efd2b1a1 100644 --- a/collections/motoko/src/rbtree.mo +++ b/collections/motoko/src/rbtree.mo @@ -3,11 +3,22 @@ import Nat64 "mo:base/Nat64"; import Iter "mo:base/Iter"; import Option "mo:base/Option"; import Random "random"; +import Profiling "../../../utils/motoko/Profiling"; actor { + stable let profiling = Profiling.init(); + var map = RBTree.RBTree(Nat64.compare); + stable var stableMap : RBTree.Tree = #leaf; let rand = Random.new(null, 42); + system func preupgrade() { + stableMap := map.share(); + }; + system func postupgrade() { + map.unshare(stableMap); + }; + public func generate(size: Nat32) : async () { let rand = Random.new(?size, 1); let iter = Iter.map(rand, func x = (x, x)); diff --git a/collections/motoko/src/splay.mo b/collections/motoko/src/splay.mo index f61a664e..f1dd6f7e 100644 --- a/collections/motoko/src/splay.mo +++ b/collections/motoko/src/splay.mo @@ -4,11 +4,22 @@ import Iter "mo:base/Iter"; import Option "mo:base/Option"; import Random "random"; import O "mo:base/Order"; +import Profiling "../../../utils/motoko/Profiling"; actor { + stable let profiling = Profiling.init(); + func compare(x: (Nat64, Nat64), y: (Nat64, Nat64)) : O.Order = Nat64.compare(x.0, y.0); var map = Splay.Splay<(Nat64, Nat64)>(compare); + stable var stableMap : [(Nat64, Nat64)] = []; let rand = Random.new(null, 42); + + system func preupgrade() { + stableMap := Iter.toArray(map.entries()); + }; + system func postupgrade() { + map.fromArray(stableMap); + }; public func generate(size: Nat32) : async () { let rand = Random.new(?size, 1); diff --git a/collections/motoko/src/triemap.mo b/collections/motoko/src/triemap.mo index e8ee377e..203c51ec 100644 --- a/collections/motoko/src/triemap.mo +++ b/collections/motoko/src/triemap.mo @@ -4,12 +4,23 @@ import Hash "mo:base/Hash"; import Iter "mo:base/Iter"; import Option "mo:base/Option"; import Random "random"; +import Profiling "../../../utils/motoko/Profiling"; actor { + stable let profiling = Profiling.init(); + func hash(x: Nat64) : Nat32 = Hash.hash(Nat64.toNat x); var map = TrieMap.TrieMap(Nat64.equal, hash); + stable var stableMap : [(Nat64, Nat64)] = []; let rand = Random.new(null, 42); + system func preupgrade() { + stableMap := Iter.toArray(map.entries()); + }; + system func postupgrade() { + map := TrieMap.fromEntries(stableMap.vals(), Nat64.equal, hash); + }; + public func generate(size: Nat32) : async () { let rand = Random.new(?size, 1); let iter = Iter.map(rand, func x = (x, x)); diff --git a/collections/motoko/src/vector.mo b/collections/motoko/src/vector.mo index bace0137..d322f2be 100644 --- a/collections/motoko/src/vector.mo +++ b/collections/motoko/src/vector.mo @@ -1,35 +1,38 @@ -import Vector "mo:vector/Class"; +import Vector "mo:vector"; import Iter "mo:base/Iter"; import Random "random"; import Nat64 "mo:base/Nat64"; import Option "mo:base/Option"; +import Profiling "../../../utils/motoko/Profiling"; actor { - var buffer = Vector.Vector(); + stable let profiling = Profiling.init(); + + stable let buffer : Vector.Vector = Vector.new(); public func generate(n: Nat) : async () { - buffer.clear(); + Vector.clear(buffer); for (_ in Iter.range(1, n)) { - buffer.add(42); + Vector.add(buffer, 42); } }; public query func get_mem() : async (Nat,Nat,Nat) { Random.get_memory() }; public func batch_get(n : Nat) : async () { - let size = buffer.size(); + let size = Vector.size(buffer); for (idx in Iter.range(1, n)) { - ignore buffer.get(idx); + ignore Vector.get(buffer, idx); } }; public func batch_put(n : Nat) : async () { for (_ in Iter.range(1, n)) { - buffer.add(42); + Vector.add(buffer, 42); } }; public func batch_remove(n : Nat) : async () { for (_ in Iter.range(1, n)) { - ignore buffer.removeLast(); + ignore Vector.removeLast(buffer); } }; } diff --git a/collections/perf.sh b/collections/perf.sh index 3573e315..5c67c89c 100644 --- a/collections/perf.sh +++ b/collections/perf.sh @@ -1,27 +1,32 @@ #!ic-repl load "../prelude.sh"; -let hashmap = wasm_profiling("motoko/.dfx/local/canisters/hashmap/hashmap.wasm"); -let triemap = wasm_profiling("motoko/.dfx/local/canisters/triemap/triemap.wasm"); -let rbtree = wasm_profiling("motoko/.dfx/local/canisters/rbtree/rbtree.wasm"); -let splay = wasm_profiling("motoko/.dfx/local/canisters/splay/splay.wasm"); -let btree = wasm_profiling("motoko/.dfx/local/canisters/btreemap/btreemap.wasm"); -let zhenya = wasm_profiling("motoko/.dfx/local/canisters/zhenya_hashmap/zhenya_hashmap.wasm"); -let heap = wasm_profiling("motoko/.dfx/local/canisters/heap/heap.wasm"); -let buffer = wasm_profiling("motoko/.dfx/local/canisters/buffer/buffer.wasm"); -let vector = wasm_profiling("motoko/.dfx/local/canisters/vector/vector.wasm"); - -let hashmap_rs = wasm_profiling("rust/.dfx/local/canisters/hashmap/hashmap.wasm"); -let btreemap_rs = wasm_profiling("rust/.dfx/local/canisters/btreemap/btreemap.wasm"); -let heap_rs = wasm_profiling("rust/.dfx/local/canisters/heap/heap.wasm"); -let imrc_hashmap_rs = wasm_profiling("rust/.dfx/local/canisters/imrc_hashmap/imrc_hashmap.wasm"); -let vector_rs = wasm_profiling("rust/.dfx/local/canisters/vector/vector.wasm"); +// use smaller page_limit to speed things up, since the whole trace is too large even with 256M. +let mo_config = record { start_page = 16; page_limit = 128 }; +let hashmap = wasm_profiling("motoko/.dfx/local/canisters/hashmap/hashmap.wasm", mo_config); +let triemap = wasm_profiling("motoko/.dfx/local/canisters/triemap/triemap.wasm", mo_config); +let rbtree = wasm_profiling("motoko/.dfx/local/canisters/rbtree/rbtree.wasm", mo_config); +let splay = wasm_profiling("motoko/.dfx/local/canisters/splay/splay.wasm", mo_config); +let btree = wasm_profiling("motoko/.dfx/local/canisters/btreemap/btreemap.wasm", mo_config); +let zhenya = wasm_profiling("motoko/.dfx/local/canisters/zhenya_hashmap/zhenya_hashmap.wasm", mo_config); +let heap = wasm_profiling("motoko/.dfx/local/canisters/heap/heap.wasm", mo_config); +let buffer = wasm_profiling("motoko/.dfx/local/canisters/buffer/buffer.wasm", mo_config); +let vector = wasm_profiling("motoko/.dfx/local/canisters/vector/vector.wasm", mo_config); + +let rs_config = record { start_page = 1; page_limit = 128 }; +let hashmap_rs = wasm_profiling("rust/.dfx/local/canisters/hashmap/hashmap.wasm", rs_config); +let btreemap_rs = wasm_profiling("rust/.dfx/local/canisters/btreemap/btreemap.wasm", rs_config); +let btreemap_stable_rs = wasm_profiling("rust/.dfx/local/canisters/btreemap_stable/btreemap_stable.wasm", rs_config); +let heap_rs = wasm_profiling("rust/.dfx/local/canisters/heap/heap.wasm", rs_config); +let heap_stable_rs = wasm_profiling("rust/.dfx/local/canisters/heap_stable/heap_stable.wasm", rs_config); +let imrc_hashmap_rs = wasm_profiling("rust/.dfx/local/canisters/imrc_hashmap/imrc_hashmap.wasm", rs_config); +let vector_rs = wasm_profiling("rust/.dfx/local/canisters/vector/vector.wasm", rs_config); +let vector_stable_rs = wasm_profiling("rust/.dfx/local/canisters/vector_stable/vector_stable.wasm", rs_config); //let movm_rs = wasm_profiling("rust/.dfx/local/canisters/movm/movm.wasm"); //let movm_dynamic_rs = wasm_profiling("rust/.dfx/local/canisters/movm_dynamic/movm_dynamic.wasm"); let file = "README.md"; -output(file, "\n## Map\n\n| |binary_size|generate 1m|max mem|batch_get 50|batch_put 50|batch_remove 50|\n|--:|--:|--:|--:|--:|--:|--:|\n"); function perf(wasm, title, init, batch) { let cid = install(wasm, encode (), null); @@ -47,15 +52,20 @@ function perf(wasm, title, init, batch) { call cid.batch_remove(batch); let svg = stringify(title, "_remove.svg"); - output(file, stringify("[", __cost__, "](",svg, ")|\n")); + output(file, stringify("[", __cost__, "](",svg, ")|")); flamegraph(cid, stringify(title, ".batch_remove"), svg); + upgrade(cid, wasm, encode ()); + let svg = stringify(title, "_upgrade.svg"); + flamegraph(cid, stringify(title, ".upgrade"), svg); + output(file, stringify("[", _, "](", svg, ")|\n")); + uninstall(cid); }; let init_size = 1_000_000; let batch_size = 50; - +output(file, "\n## Map\n\n| |binary_size|generate 1m|max mem|batch_get 50|batch_put 50|batch_remove 50|upgrade|\n|--:|--:|--:|--:|--:|--:|--:|--:|\n"); perf(hashmap, "hashmap", init_size, batch_size); perf(triemap, "triemap", init_size, batch_size); perf(rbtree, "rbtree", init_size, batch_size); @@ -66,17 +76,26 @@ perf(btreemap_rs, "btreemap_rs", init_size, batch_size); perf(imrc_hashmap_rs, "imrc_hashmap_rs", init_size, batch_size); perf(hashmap_rs, "hashmap_rs", init_size, batch_size); -output(file, "\n## Priority queue\n\n| |binary_size|heapify 1m|max mem|pop_min 50|put 50|\n|--:|--:|--:|--:|--:|--:|\n"); +output(file, "\n## Priority queue\n\n| |binary_size|heapify 1m|max mem|pop_min 50|put 50|pop_min 50|upgrade|\n|--:|--:|--:|--:|--:|--:|--:|--:|\n"); perf(heap, "heap", init_size, batch_size); perf(heap_rs, "heap_rs", init_size, batch_size); let init_size = 5_000; let batch_size = 500; -output(file, "\n## Growable array\n\n| |binary_size|generate 5k|max mem|batch_get 500|batch_put 500|batch_remove 500|\n|--:|--:|--:|--:|--:|--:|--:|\n"); +output(file, "\n## Growable array\n\n| |binary_size|generate 5k|max mem|batch_get 500|batch_put 500|batch_remove 500|upgrade|\n|--:|--:|--:|--:|--:|--:|--:|--:|\n"); perf(buffer, "buffer", init_size, batch_size); perf(vector, "vector", init_size, batch_size); perf(vector_rs, "vec_rs", init_size, batch_size); +let init_size = 50_000; +let batch_size = 50; +output(file, "\n## Stable structures\n\n| |binary_size|generate 50k|max mem|batch_get 50|batch_put 50|batch_remove 50|upgrade|\n|--:|--:|--:|--:|--:|--:|--:|--:|\n"); +perf(btreemap_rs, "btreemap_rs", init_size, batch_size); +perf(btreemap_stable_rs, "btreemap_stable_rs", init_size, batch_size); +perf(heap_rs, "heap_rs", init_size, batch_size); +perf(heap_stable_rs, "heap_stable_rs", init_size, batch_size); +perf(vector_rs, "vec_rs", init_size, batch_size); +perf(vector_stable_rs, "vec_stable_rs", init_size, batch_size); /* let movm_size = 10000; diff --git a/collections/rust/dfx.json b/collections/rust/dfx.json index 3d674115..735b7ddb 100644 --- a/collections/rust/dfx.json +++ b/collections/rust/dfx.json @@ -11,11 +11,21 @@ "candid": "collection.did", "package": "btreemap" }, + "btreemap_stable": { + "type": "rust", + "candid": "collection.did", + "package": "btreemap_stable" + }, "heap": { "type": "rust", "candid": "collection.did", "package": "heap" }, + "heap_stable": { + "type": "rust", + "candid": "collection.did", + "package": "heap_stable" + }, "imrc_hashmap": { "type": "rust", "candid": "collection.did", @@ -25,6 +35,11 @@ "type": "rust", "candid": "collection.did", "package": "vector" + }, + "vector_stable": { + "type": "rust", + "candid": "collection.did", + "package": "vector_stable" } }, "defaults": { diff --git a/collections/rust/src/btreemap/Cargo.toml b/collections/rust/src/btreemap/Cargo.toml index 0f5ac189..59c6646f 100644 --- a/collections/rust/src/btreemap/Cargo.toml +++ b/collections/rust/src/btreemap/Cargo.toml @@ -10,5 +10,5 @@ crate-type = ["cdylib"] [dependencies] candid.workspace = true -ic-cdk.workspace = true - +ic-cdk.workspace = true +utils = { path = "../../../../utils/rust" } diff --git a/collections/rust/src/btreemap/src/lib.rs b/collections/rust/src/btreemap/src/lib.rs index acce646a..6e5f0ae3 100644 --- a/collections/rust/src/btreemap/src/lib.rs +++ b/collections/rust/src/btreemap/src/lib.rs @@ -1,39 +1,26 @@ use std::cell::RefCell; use std::collections::BTreeMap; - -struct Random { - state: u64, - size: Option, - ind: u32, -} -impl Random { - pub fn new(size: Option, seed: u64) -> Self { - Random { - state: seed, - size, - ind: 0, - } - } -} -impl Iterator for Random { - type Item = u64; - fn next(&mut self) -> Option { - if let Some(size) = self.size { - self.ind += 1; - if self.ind > size { - return None; - } - } - self.state = self.state * 48271 % 0x7fffffff; - Some(self.state) - } -} +use utils::Random; thread_local! { static MAP: RefCell> = RefCell::default(); static RAND: RefCell = RefCell::new(Random::new(None, 42)); } +#[ic_cdk::init] +fn init() { + utils::profiling_init(); +} +#[ic_cdk::pre_upgrade] +fn pre_upgrade() { + MAP.with(|map| utils::save_stable(&map)); +} +#[ic_cdk::post_upgrade] +fn post_upgrade() { + let value: BTreeMap = utils::restore_stable(); + MAP.with(|cell| *cell.borrow_mut() = value); +} + #[ic_cdk::update] fn generate(size: u32) { let rand = Random::new(Some(size), 1); @@ -48,8 +35,7 @@ fn generate(size: u32) { #[ic_cdk::query] fn get_mem() -> (u128, u128, u128) { - let size = core::arch::wasm32::memory_size(0) as u128 * 32768; - (size, size, size) + utils::get_mem() } #[ic_cdk::update] diff --git a/collections/rust/src/btreemap_stable/Cargo.toml b/collections/rust/src/btreemap_stable/Cargo.toml new file mode 100644 index 00000000..8dc00efb --- /dev/null +++ b/collections/rust/src/btreemap_stable/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "btreemap_stable" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[lib] +crate-type = ["cdylib"] + +[dependencies] +candid.workspace = true +ic-cdk.workspace = true +ic-stable-structures.workspace = true +utils = { path = "../../../../utils/rust" } diff --git a/collections/rust/src/btreemap_stable/src/lib.rs b/collections/rust/src/btreemap_stable/src/lib.rs new file mode 100644 index 00000000..f107678d --- /dev/null +++ b/collections/rust/src/btreemap_stable/src/lib.rs @@ -0,0 +1,75 @@ +use ic_stable_structures::StableBTreeMap; +use std::cell::RefCell; +use utils::{Memory, Random}; + +thread_local! { + static MAP: RefCell> = RefCell::new(StableBTreeMap::init(utils::get_upgrade_memory())); + static RAND: RefCell = RefCell::new(Random::new(None, 42)); +} + +#[ic_cdk::init] +fn init() { + utils::profiling_init(); +} + +#[ic_cdk::post_upgrade] +fn post_upgrade() { + MAP.with(|map| drop(map.borrow())); +} + +#[ic_cdk::update] +fn generate(size: u32) { + let rand = Random::new(Some(size), 1); + let iter = rand.map(|x| (x, x)); + MAP.with(|map| { + let mut map = map.borrow_mut(); + for (k, v) in iter { + map.insert(k, v); + } + }); +} + +#[ic_cdk::query] +fn get_mem() -> (u128, u128, u128) { + utils::get_upgrade_mem_size() +} + +#[ic_cdk::update] +fn batch_get(n: u32) { + MAP.with(|map| { + let map = map.borrow(); + RAND.with(|rand| { + let mut rand = rand.borrow_mut(); + for _ in 0..n { + let k = rand.next().unwrap(); + map.get(&k); + } + }) + }) +} + +#[ic_cdk::update] +fn batch_put(n: u32) { + MAP.with(|map| { + let mut map = map.borrow_mut(); + RAND.with(|rand| { + let mut rand = rand.borrow_mut(); + for _ in 0..n { + let k = rand.next().unwrap(); + map.insert(k, k); + } + }) + }) +} + +#[ic_cdk::update] +fn batch_remove(n: u32) { + let mut rand = Random::new(None, 1); + MAP.with(|map| { + let mut map = map.borrow_mut(); + for _ in 0..n { + let k = rand.next().unwrap(); + map.remove(&k); + } + }) +} diff --git a/collections/rust/src/hashmap/Cargo.toml b/collections/rust/src/hashmap/Cargo.toml index d8c26cf4..9db225ba 100644 --- a/collections/rust/src/hashmap/Cargo.toml +++ b/collections/rust/src/hashmap/Cargo.toml @@ -11,4 +11,5 @@ crate-type = ["cdylib"] [dependencies] candid.workspace = true ic-cdk.workspace = true +utils = { path = "../../../../utils/rust" } fxhash = "0.2.1" diff --git a/collections/rust/src/hashmap/src/lib.rs b/collections/rust/src/hashmap/src/lib.rs index 74c8ddae..d97eed66 100644 --- a/collections/rust/src/hashmap/src/lib.rs +++ b/collections/rust/src/hashmap/src/lib.rs @@ -1,33 +1,6 @@ use fxhash::FxHashMap; use std::cell::RefCell; - -struct Random { - state: u64, - size: Option, - ind: u32, -} -impl Random { - pub fn new(size: Option, seed: u64) -> Self { - Random { - state: seed, - size, - ind: 0, - } - } -} -impl Iterator for Random { - type Item = u64; - fn next(&mut self) -> Option { - if let Some(size) = self.size { - self.ind += 1; - if self.ind > size { - return None; - } - } - self.state = self.state * 48271 % 0x7fffffff; - Some(self.state) - } -} +use utils::Random; thread_local! { // FxHashMap uses the same std::collections::HashMap with a deterministic hasher @@ -35,6 +8,20 @@ thread_local! { static RAND: RefCell = RefCell::new(Random::new(None, 42)); } +#[ic_cdk::init] +fn init() { + utils::profiling_init(); +} +#[ic_cdk::pre_upgrade] +fn pre_upgrade() { + MAP.with(|map| utils::save_stable(&map)); +} +#[ic_cdk::post_upgrade] +fn post_upgrade() { + let value: FxHashMap = utils::restore_stable(); + MAP.with(|cell| *cell.borrow_mut() = value); +} + #[ic_cdk::update] fn generate(size: u32) { let rand = Random::new(Some(size), 1); @@ -49,8 +36,7 @@ fn generate(size: u32) { #[ic_cdk::query] fn get_mem() -> (u128, u128, u128) { - let size = core::arch::wasm32::memory_size(0) as u128 * 32768; - (size, size, size) + utils::get_mem() } #[ic_cdk::update] diff --git a/collections/rust/src/heap/Cargo.toml b/collections/rust/src/heap/Cargo.toml index 94113294..412c7958 100644 --- a/collections/rust/src/heap/Cargo.toml +++ b/collections/rust/src/heap/Cargo.toml @@ -11,3 +11,4 @@ crate-type = ["cdylib"] [dependencies] candid.workspace = true ic-cdk.workspace = true +utils = { path = "../../../../utils/rust" } diff --git a/collections/rust/src/heap/src/lib.rs b/collections/rust/src/heap/src/lib.rs index 4af237ca..566f122e 100644 --- a/collections/rust/src/heap/src/lib.rs +++ b/collections/rust/src/heap/src/lib.rs @@ -1,44 +1,31 @@ use std::cell::RefCell; use std::cmp::Reverse; use std::collections::BinaryHeap; - -struct Random { - state: u64, - size: Option, - ind: u32, -} -impl Random { - pub fn new(size: Option, seed: u64) -> Self { - Random { - state: seed, - size, - ind: 0, - } - } -} -impl Iterator for Random { - type Item = u64; - fn next(&mut self) -> Option { - if let Some(size) = self.size { - self.ind += 1; - if self.ind > size { - return None; - } - } - self.state = self.state * 48271 % 0x7fffffff; - Some(self.state) - } -} +use utils::Random; thread_local! { static MAP: RefCell>> = RefCell::default(); static RAND: RefCell = RefCell::new(Random::new(None, 42)); } +#[ic_cdk::init] +fn init() { + utils::profiling_init(); +} +#[ic_cdk::pre_upgrade] +fn pre_upgrade() { + MAP.with(|map| utils::save_stable(&map)); +} +#[ic_cdk::post_upgrade] +fn post_upgrade() { + let value: BinaryHeap> = utils::restore_stable(); + MAP.with(|cell| *cell.borrow_mut() = value); +} + #[ic_cdk::update] fn generate(size: u32) { let rand = Random::new(Some(size), 1); - let iter = rand.map(|x| Reverse(x)); + let iter = rand.map(Reverse); MAP.with(|map| { let mut map = map.borrow_mut(); *map = iter.collect::>>(); @@ -47,8 +34,7 @@ fn generate(size: u32) { #[ic_cdk::query] fn get_mem() -> (u128, u128, u128) { - let size = core::arch::wasm32::memory_size(0) as u128 * 32768; - (size, size, size) + utils::get_mem() } #[ic_cdk::update] diff --git a/collections/rust/src/heap_stable/Cargo.toml b/collections/rust/src/heap_stable/Cargo.toml new file mode 100644 index 00000000..d7775f7a --- /dev/null +++ b/collections/rust/src/heap_stable/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "heap_stable" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[lib] +crate-type = ["cdylib"] + +[dependencies] +candid.workspace = true +ic-cdk.workspace = true +ic-stable-structures.workspace = true +utils = { path = "../../../../utils/rust" } diff --git a/collections/rust/src/heap_stable/src/lib.rs b/collections/rust/src/heap_stable/src/lib.rs new file mode 100644 index 00000000..e63e1420 --- /dev/null +++ b/collections/rust/src/heap_stable/src/lib.rs @@ -0,0 +1,70 @@ +use ic_stable_structures::StableMinHeap; +use std::cell::RefCell; +use std::cmp::Reverse; +use utils::{Memory, Random}; + +thread_local! { + static MAP: RefCell, Memory>> = RefCell::new(StableMinHeap::init(utils::get_upgrade_memory()).unwrap()); + static RAND: RefCell = RefCell::new(Random::new(None, 42)); +} + +#[ic_cdk::init] +fn init() { + utils::profiling_init(); +} + +#[ic_cdk::post_upgrade] +fn post_upgrade() { + MAP.with(|map| drop(map.borrow())); +} + +#[ic_cdk::update] +fn generate(size: u32) { + let rand = Random::new(Some(size), 1); + let iter = rand.map(Reverse); + MAP.with(|map| { + let mut map = map.borrow_mut(); + for x in iter { + map.push(&x).unwrap(); + } + }); +} + +#[ic_cdk::query] +fn get_mem() -> (u128, u128, u128) { + utils::get_upgrade_mem_size() +} + +#[ic_cdk::update] +fn batch_get(n: u32) { + MAP.with(|map| { + let mut map = map.borrow_mut(); + for _ in 0..n { + map.pop(); + } + }) +} + +#[ic_cdk::update] +fn batch_put(n: u32) { + MAP.with(|map| { + let mut map = map.borrow_mut(); + RAND.with(|rand| { + let mut rand = rand.borrow_mut(); + for _ in 0..n { + let k = rand.next().unwrap(); + map.push(&Reverse(k)).unwrap(); + } + }) + }) +} + +#[ic_cdk::update] +fn batch_remove(n: u32) { + MAP.with(|map| { + let mut map = map.borrow_mut(); + for _ in 0..n { + map.pop(); + } + }) +} diff --git a/collections/rust/src/imrc_hashmap/Cargo.toml b/collections/rust/src/imrc_hashmap/Cargo.toml index 635c173d..6df2770b 100644 --- a/collections/rust/src/imrc_hashmap/Cargo.toml +++ b/collections/rust/src/imrc_hashmap/Cargo.toml @@ -11,4 +11,5 @@ crate-type = ["cdylib"] [dependencies] candid.workspace = true ic-cdk.workspace = true +utils = { path = "../../../../utils/rust" } im-rc = "15.1.0" diff --git a/collections/rust/src/imrc_hashmap/src/lib.rs b/collections/rust/src/imrc_hashmap/src/lib.rs index 7651d33d..9855ac50 100644 --- a/collections/rust/src/imrc_hashmap/src/lib.rs +++ b/collections/rust/src/imrc_hashmap/src/lib.rs @@ -1,39 +1,30 @@ use im_rc::HashMap; use std::cell::RefCell; - -struct Random { - state: u64, - size: Option, - ind: u32, -} -impl Random { - pub fn new(size: Option, seed: u64) -> Self { - Random { - state: seed, - size, - ind: 0, - } - } -} -impl Iterator for Random { - type Item = u64; - fn next(&mut self) -> Option { - if let Some(size) = self.size { - self.ind += 1; - if self.ind > size { - return None; - } - } - self.state = self.state * 48271 % 0x7fffffff; - Some(self.state) - } -} +use utils::Random; thread_local! { static MAP: RefCell> = RefCell::default(); static RAND: RefCell = RefCell::new(Random::new(None, 42)); } +#[ic_cdk::init] +fn init() { + utils::profiling_init(); +} +#[ic_cdk::pre_upgrade] +fn pre_upgrade() { + MAP.with(|map| { + let map = map.borrow(); + let vec: Vec<_> = map.iter().collect(); + utils::save_stable(&vec); + }); +} +#[ic_cdk::post_upgrade] +fn post_upgrade() { + let value: Vec<(u64, u64)> = utils::restore_stable(); + MAP.with(|cell| *cell.borrow_mut() = value.into_iter().collect()); +} + #[ic_cdk::update] fn generate(size: u32) { let rand = Random::new(Some(size), 1); @@ -48,8 +39,7 @@ fn generate(size: u32) { #[ic_cdk::query] fn get_mem() -> (u128, u128, u128) { - let size = core::arch::wasm32::memory_size(0) as u128 * 32768; - (size, size, size) + utils::get_mem() } #[ic_cdk::update] @@ -60,7 +50,7 @@ fn batch_get(n: u32) { let mut rand = rand.borrow_mut(); for _ in 0..n { let k = rand.next().unwrap(); - drop(map.get(&k)); + let _ = map.get(&k); } }) }) diff --git a/collections/rust/src/vector/Cargo.toml b/collections/rust/src/vector/Cargo.toml index b4f5b322..0430adbf 100644 --- a/collections/rust/src/vector/Cargo.toml +++ b/collections/rust/src/vector/Cargo.toml @@ -11,3 +11,4 @@ crate-type = ["cdylib"] [dependencies] candid.workspace = true ic-cdk.workspace = true +utils = { path = "../../../../utils/rust" } diff --git a/collections/rust/src/vector/src/lib.rs b/collections/rust/src/vector/src/lib.rs index fa8e2596..141f8a8e 100644 --- a/collections/rust/src/vector/src/lib.rs +++ b/collections/rust/src/vector/src/lib.rs @@ -4,6 +4,20 @@ thread_local! { static MAP: RefCell> = RefCell::default(); } +#[ic_cdk::init] +fn init() { + utils::profiling_init(); +} +#[ic_cdk::pre_upgrade] +fn pre_upgrade() { + MAP.with(|map| utils::save_stable(&map)); +} +#[ic_cdk::post_upgrade] +fn post_upgrade() { + let value: Vec = utils::restore_stable(); + MAP.with(|cell| *cell.borrow_mut() = value); +} + #[ic_cdk::update] fn generate(size: u32) { MAP.with(|map| { @@ -17,8 +31,7 @@ fn generate(size: u32) { #[ic_cdk::query] fn get_mem() -> (u128, u128, u128) { - let size = core::arch::wasm32::memory_size(0) as u128 * 32768; - (size, size, size) + utils::get_mem() } #[ic_cdk::update] diff --git a/collections/rust/src/vector_stable/Cargo.toml b/collections/rust/src/vector_stable/Cargo.toml new file mode 100644 index 00000000..8b667122 --- /dev/null +++ b/collections/rust/src/vector_stable/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "vector_stable" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[lib] +crate-type = ["cdylib"] + +[dependencies] +candid.workspace = true +ic-cdk.workspace = true +ic-stable-structures.workspace = true +utils = { path = "../../../../utils/rust" } diff --git a/collections/rust/src/vector_stable/src/lib.rs b/collections/rust/src/vector_stable/src/lib.rs new file mode 100644 index 00000000..e5ca1e1d --- /dev/null +++ b/collections/rust/src/vector_stable/src/lib.rs @@ -0,0 +1,62 @@ +use ic_stable_structures::StableVec; +use std::cell::RefCell; +use utils::Memory; + +thread_local! { + static MAP: RefCell> = RefCell::new(StableVec::init(utils::get_upgrade_memory()).unwrap()); +} + +#[ic_cdk::init] +fn init() { + utils::profiling_init(); +} + +#[ic_cdk::post_upgrade] +fn post_upgrade() { + MAP.with(|map| drop(map.borrow())); +} + +#[ic_cdk::update] +fn generate(size: u32) { + MAP.with(|map| { + let map = map.borrow_mut(); + for _ in 0..size { + map.push(&42).unwrap(); + } + }); +} + +#[ic_cdk::query] +fn get_mem() -> (u128, u128, u128) { + utils::get_upgrade_mem_size() +} + +#[ic_cdk::update] +fn batch_get(n: u32) { + MAP.with(|map| { + let map = map.borrow(); + for idx in 0..n { + let _ = map.get(idx as u64); + } + }) +} + +#[ic_cdk::update] +fn batch_put(n: u32) { + MAP.with(|map| { + let map = map.borrow_mut(); + for _ in 0..n { + map.push(&42).unwrap(); + } + }) +} + +#[ic_cdk::update] +fn batch_remove(n: u32) { + MAP.with(|map| { + let map = map.borrow_mut(); + for _ in 0..n { + map.pop(); + } + }) +} diff --git a/crypto/Makefile b/crypto/Makefile index 20f25a0b..4714a4f7 100644 --- a/crypto/Makefile +++ b/crypto/Makefile @@ -1,34 +1,15 @@ -APP = crypto +include ../*.mk .PHONY: all motoko rust build perf all: build perf motoko: - set -e; \ - cd motoko; \ - envsubst < mops.template.toml > mops.toml; \ - mops install; \ - dfx canister create --all; \ - dfx build; \ - echo "Optimize with ic-wasm level 3"; \ - for f in .dfx/local/canisters/*/*.wasm; do ic-wasm -o $$f $$f shrink --optimize O3 --keep-name-section; done; \ - rm mops.toml; \ - cd .. + $(call build_with_mops,motoko) rust: - set -e; \ - cd rust; \ - dfx canister create --all; \ - dfx build; \ - echo "Optimize with ic-wasm level 3"; \ - for f in .dfx/local/canisters/*/*.wasm; do ic-wasm -o $$f $$f shrink --optimize O3 --keep-name-section; done; \ - cd .. + $(call build,rust) build: motoko rust perf: - set -e; \ - mkdir -p ../_out/$(APP); \ - cp README.md ../_out/$(APP); \ - cd ../_out/$(APP); \ - ic-repl ../../$(APP)/perf.sh + $(call perf,crypto,perf.sh) diff --git a/crypto/README.md b/crypto/README.md index 56d93e7e..b201e42c 100644 --- a/crypto/README.md +++ b/crypto/README.md @@ -11,3 +11,4 @@ Measure different cryptographic libraries written in both Motoko and Rust. + `max mem`. For Motoko, it reports `rts_max_heap_size` after `generate` call; For Rust, it reports the Wasm's memory page * 32Kb. + `inc`. Increment a counter and insert the counter value into the map. + `witness`. Generate the root hash and a witness for the counter. + + `upgrade`. Upgrade the canister with the same Wasm. In Motoko, we use stable variable. In Rust, we convert the tree to a vector before serialization. diff --git a/crypto/motoko/mops.template.toml b/crypto/motoko/mops.template.toml index 3f3c3a8a..5ee8e32d 100644 --- a/crypto/motoko/mops.template.toml +++ b/crypto/motoko/mops.template.toml @@ -1,4 +1,4 @@ [dependencies] base = "https://github.com/dfinity/motoko-base#moc-$MOC_VERSION" -sha2 = "0.0.2" -ic-certification = "0.1.1" +sha2 = "0.0.4" +ic-certification = "0.1.3" diff --git a/crypto/motoko/src/certified_map.mo b/crypto/motoko/src/certified_map.mo index c9cede06..dc78e91a 100644 --- a/crypto/motoko/src/certified_map.mo +++ b/crypto/motoko/src/certified_map.mo @@ -3,10 +3,13 @@ import Blob "mo:base/Blob"; import { tabulate } "mo:base/Array"; import { range } "mo:base/Iter"; import Utils "utils"; +import Profiling "../../../utils/motoko/Profiling"; actor { + stable let profiling = Profiling.init(); + var counter : Nat8 = 0; - let cert_store : CertTree.Store = CertTree.newStore(); + stable let cert_store : CertTree.Store = CertTree.newStore(); let tree = CertTree.Ops(cert_store); public func generate(size : Nat) : async () { diff --git a/crypto/perf.sh b/crypto/perf.sh index b9920b26..968ec0b5 100644 --- a/crypto/perf.sh +++ b/crypto/perf.sh @@ -3,8 +3,8 @@ load "../prelude.sh"; let sha_mo = wasm_profiling("motoko/.dfx/local/canisters/sha/sha.wasm"); let sha_rs = wasm_profiling("rust/.dfx/local/canisters/sha/sha.wasm"); -let map_mo = wasm_profiling("motoko/.dfx/local/canisters/certified_map/certified_map.wasm"); -let map_rs = wasm_profiling("rust/.dfx/local/canisters/certified_map/certified_map.wasm"); +let map_mo = wasm_profiling("motoko/.dfx/local/canisters/certified_map/certified_map.wasm", mo_config); +let map_rs = wasm_profiling("rust/.dfx/local/canisters/certified_map/certified_map.wasm", rs_config); let sample = file("sample_wasm.bin"); let file = "README.md"; @@ -59,6 +59,11 @@ function perf_map(wasm, title, init_size) { output(file, stringify("[", __cost_witness, "](", svg, ")|")); flamegraph(cid, stringify(title, ".witness()"), svg); + upgrade(cid, wasm, encode ()); + let svg = stringify(title, "_upgrade.svg"); + flamegraph(cid, stringify(title, ".upgrade"), svg); + output(file, stringify("[", _, "](", svg, ")|")); + output(file, "\n"); uninstall(cid); witness; @@ -68,7 +73,7 @@ let res1 = perf_sha(sha_mo, "Motoko"); let res2 = perf_sha(sha_rs, "Rust"); assert res1 == res2; -output(file, "\n## Certified map\n\n| |binary_size|generate 10k|max mem|inc|witness|\n|--:|--:|--:|--:|--:|--:|\n"); +output(file, "\n## Certified map\n\n| |binary_size|generate 10k|max mem|inc|witness|upgrade|\n|--:|--:|--:|--:|--:|--:|--:|\n"); let init_size = 10_000; let res1 = perf_map(map_mo, "Motoko", init_size); let res2 = perf_map(map_rs, "Rust", init_size); diff --git a/crypto/rust/src/certified_map/Cargo.toml b/crypto/rust/src/certified_map/Cargo.toml index e9d3ac9c..5d15a5e3 100644 --- a/crypto/rust/src/certified_map/Cargo.toml +++ b/crypto/rust/src/certified_map/Cargo.toml @@ -12,6 +12,7 @@ crate-type = ["cdylib"] candid.workspace = true ic-cdk.workspace = true ic-certified-map = "0.4" +utils = { path = "../../../../utils/rust" } serde_bytes = "0.11" serde_cbor = "0.11" serde = "1" diff --git a/crypto/rust/src/certified_map/src/lib.rs b/crypto/rust/src/certified_map/src/lib.rs index 976e0cee..3f7769f3 100644 --- a/crypto/rust/src/certified_map/src/lib.rs +++ b/crypto/rust/src/certified_map/src/lib.rs @@ -1,5 +1,5 @@ use ic_certified_map::RbTree; -use serde_bytes::ByteBuf; +use serde_bytes::{ByteBuf, Bytes}; use std::cell::{Cell, RefCell}; struct Random { @@ -27,10 +27,10 @@ impl Iterator for Word { type Item = [u8; 7]; fn next(&mut self) -> Option { let mut res: Self::Item = [0; 7]; - for i in 0..7 { + for item in &mut res { let x = self.0.next()?; let x = x % 57 + 65; - res[i] = x as u8; + *item = x as u8; } Some(res) } @@ -41,6 +41,32 @@ thread_local! { static TREE: RefCell, Vec>> = RefCell::new(RbTree::new()); } +#[ic_cdk::init] +fn init() { + utils::profiling_init(); +} +#[ic_cdk::pre_upgrade] +fn pre_upgrade() { + TREE.with(|tree| { + let tree = tree.borrow(); + let vec: Vec<(&Bytes, &Bytes)> = tree + .iter() + .map(|(a, b)| (Bytes::new(a), Bytes::new(b))) + .collect(); + utils::save_stable(&vec) + }); +} +#[ic_cdk::post_upgrade] +fn post_upgrade() { + let value: Vec<(ByteBuf, ByteBuf)> = utils::restore_stable(); + TREE.with(|cell| { + *cell.borrow_mut() = value + .into_iter() + .map(|(a, b)| (a.into_vec(), b.into_vec())) + .collect() + }); +} + #[ic_cdk::update] fn generate(size: u64) { let mut iter = Word::new(1); @@ -55,8 +81,7 @@ fn generate(size: u64) { #[ic_cdk::query] fn get_mem() -> (u128, u128, u128) { - let size = core::arch::wasm32::memory_size(0) as u128 * 32768; - (size, size, size) + utils::get_mem() } #[ic_cdk::update] diff --git a/crypto/rust/src/sha/src/lib.rs b/crypto/rust/src/sha/src/lib.rs index 3e1b1140..0b890d55 100644 --- a/crypto/rust/src/sha/src/lib.rs +++ b/crypto/rust/src/sha/src/lib.rs @@ -8,11 +8,11 @@ const ACCOUNT_SEPERATOR: &[u8] = b"\x0Aaccount-id"; #[ic_cdk::update] fn sha256(blob: ByteBuf) -> ByteBuf { - ByteBuf::from(&mut sha2::Sha256::digest(&blob)[..]) + ByteBuf::from(&mut sha2::Sha256::digest(blob)[..]) } #[ic_cdk::update] fn sha512(blob: ByteBuf) -> ByteBuf { - ByteBuf::from(&mut sha2::Sha512::digest(&blob)[..]) + ByteBuf::from(&mut sha2::Sha512::digest(blob)[..]) } #[ic_cdk::update] fn principalToAccount(id: Principal) -> ByteBuf { @@ -25,9 +25,9 @@ fn principalToAccount(id: Principal) -> ByteBuf { #[ic_cdk::update] fn principalToNeuron(id: Principal, nonce: u64) -> ByteBuf { let mut data = Sha256::new(); - data.update(&[0x0c]); + data.update([0x0c]); data.update(b"neuron-stake"); data.update(id.as_slice()); - data.update(&nonce.to_be_bytes()); + data.update(nonce.to_be_bytes()); ByteBuf::from(&mut data.finalize()[..]) } diff --git a/dapps/Makefile b/dapps/Makefile index d60b6bd1..ad2edcc5 100644 --- a/dapps/Makefile +++ b/dapps/Makefile @@ -1,30 +1,15 @@ -APP = dapps +include ../*.mk .PHONY: all motoko rust build perf all: build perf motoko: - cd motoko; \ - dfx canister create --all; \ - dfx build; \ - echo "Optimize with ic-wasm level 3"; \ - for f in .dfx/local/canisters/*/*.wasm; do ic-wasm -o $$f $$f shrink --optimize O3 --keep-name-section; done; \ - cd .. + $(call build_with_mops,motoko) rust: - cd rust; \ - dfx canister create --all; \ - dfx build; \ - echo "Optimize with ic-wasm level 3"; \ - for f in .dfx/local/canisters/*/*.wasm; do ic-wasm -o $$f $$f shrink --optimize O3 --keep-name-section; done; \ - cd .. + $(call build,rust) build: motoko rust perf: - set -e; \ - mkdir -p ../_out/$(APP); \ - cp README.md ../_out/$(APP); \ - cd ../_out/$(APP); \ - ic-repl ../../$(APP)/basic_dao.sh; \ - ic-repl ../../$(APP)/nft.sh + $(call perf_two,dapps,basic_dao.sh,nft.sh) diff --git a/dapps/basic_dao.sh b/dapps/basic_dao.sh index b67f005f..46f4d0d1 100644 --- a/dapps/basic_dao.sh +++ b/dapps/basic_dao.sh @@ -8,14 +8,14 @@ identity cathy; identity dory; identity genesis; -let motoko = wasm_profiling("motoko/.dfx/local/canisters/basic_dao/basic_dao.wasm"); -let rust = wasm_profiling("rust/.dfx/local/canisters/basic_dao/basic_dao.wasm"); +let motoko = wasm_profiling("motoko/.dfx/local/canisters/basic_dao/basic_dao.wasm", mo_config); +let rust = wasm_profiling("rust/.dfx/local/canisters/basic_dao/basic_dao.wasm", rs_config); let file = "README.md"; -output(file, "\n## Basic DAO\n\n| |binary_size|init|transfer_token|submit_proposal|vote_proposal|\n|--|--:|--:|--:|--:|--:|\n"); +output(file, "\n## Basic DAO\n\n| |binary_size|init|transfer_token|submit_proposal|vote_proposal|upgrade|\n|--|--:|--:|--:|--:|--:|--:|\n"); -function perf(wasm, title, init_args) { -let init = encode init_args.__init_args( +function perf(wasm, title) { +let init = encode wasm.__init_args( record { accounts = vec { record { owner = genesis; tokens = record { amount_e8s = 1_000_000_000_000 } }; @@ -61,12 +61,18 @@ flamegraph(DAO, "DAO.submit_proposal", svg); identity bob; call DAO.vote(record { proposal_id = alice_id; vote = variant { Yes } }); let svg = stringify(title, "_vote.svg"); -output(file, stringify("[", __cost__, "](", svg, ")|\n")); +output(file, stringify("[", __cost__, "](", svg, ")|")); flamegraph(DAO, "DAO.vote", svg); -}; -import init = "2vxsx-fae" as "motoko/.dfx/local/canisters/basic_dao/constructor.did"; -perf(motoko, "Motoko", init); -import init = "2vxsx-fae" as "rust/.dfx/local/canisters/basic_dao/constructor.did"; -perf(rust, "Rust", init); +// upgrade +identity genesis; +upgrade(DAO, wasm, init); +let svg = stringify(title, "_upgrade.svg"); +flamegraph(DAO, "DAO.upgrade", svg); +output(file, stringify("[", _, "](", svg, ")|\n")); +call DAO.get_proposal(0); +assert _?.proposer == alice; +}; +perf(motoko, "Motoko"); +perf(rust, "Rust"); diff --git a/dapps/motoko/basic_dao/Main.mo b/dapps/motoko/basic_dao/Main.mo index a69c0672..0e0e3704 100644 --- a/dapps/motoko/basic_dao/Main.mo +++ b/dapps/motoko/basic_dao/Main.mo @@ -9,8 +9,11 @@ import ICRaw "mo:base/ExperimentalInternetComputer"; import List "mo:base/List"; import Time "mo:base/Time"; import Types "./Types"; +import Profiling "../../../utils/motoko/Profiling"; shared actor class DAO(init : Types.BasicDaoStableStorage) = Self { + stable let profiling = Profiling.init(); + stable var accounts = Types.accounts_fromArray(init.accounts); stable var proposals = Types.proposals_fromArray(init.proposals); stable var next_proposal_id : Nat = 0; diff --git a/dapps/motoko/dfx.json b/dapps/motoko/dfx.json index 6db884dd..c3efa2db 100644 --- a/dapps/motoko/dfx.json +++ b/dapps/motoko/dfx.json @@ -6,5 +6,11 @@ "basic_dao": { "main": "basic_dao/Main.mo" } + }, + "defaults": { + "build": { + "packtool": "mops sources", + "args": "" + } } } diff --git a/dapps/motoko/dip721-nft/Main.mo b/dapps/motoko/dip721-nft/Main.mo index 533c7111..928c31a3 100644 --- a/dapps/motoko/dip721-nft/Main.mo +++ b/dapps/motoko/dip721-nft/Main.mo @@ -9,8 +9,11 @@ import Option "mo:base/Option"; import Bool "mo:base/Bool"; import Principal "mo:base/Principal"; import Types "./Types"; +import Profiling "../../../utils/motoko/Profiling"; shared (creator) actor class Dip721NFT(init : Types.Dip721NonFungibleToken) = Self { + stable let profiling = Profiling.init(); + stable var transactionId: Types.TransactionId = 0; stable var nfts = List.nil(); stable var custodians = List.make(creator.caller); diff --git a/dapps/motoko/mops.template.toml b/dapps/motoko/mops.template.toml new file mode 100644 index 00000000..976184ae --- /dev/null +++ b/dapps/motoko/mops.template.toml @@ -0,0 +1,2 @@ +[dependencies] +base = "https://github.com/dfinity/motoko-base#moc-$MOC_VERSION" diff --git a/dapps/nft.sh b/dapps/nft.sh index 7784618c..b1602b01 100644 --- a/dapps/nft.sh +++ b/dapps/nft.sh @@ -1,20 +1,18 @@ #!ic-repl load "../prelude.sh"; -import fake = "2vxsx-fae" as "motoko/.dfx/local/canisters/dip721_nft/constructor.did"; - // Setup initial account identity alice; identity bob; -let motoko = wasm_profiling("motoko/.dfx/local/canisters/dip721_nft/dip721_nft.wasm"); -let rust = wasm_profiling("rust/.dfx/local/canisters/dip721_nft/dip721_nft.wasm"); +let motoko = wasm_profiling("motoko/.dfx/local/canisters/dip721_nft/dip721_nft.wasm", mo_config); +let rust = wasm_profiling("rust/.dfx/local/canisters/dip721_nft/dip721_nft.wasm", rs_config); let file = "README.md"; -output(file, "\n## DIP721 NFT\n\n| |binary_size|init|mint_token|transfer_token|\n|--|--:|--:|--:|--:|\n"); +output(file, "\n## DIP721 NFT\n\n| |binary_size|init|mint_token|transfer_token|upgrade|\n|--|--:|--:|--:|--:|--:|\n"); function perf(wasm, title) { - let init = encode fake.__init_args( + let init = encode wasm.__init_args( record { logo = record { logo_type = "image/png"; @@ -50,8 +48,13 @@ function perf(wasm, title) { // transfer tokens let _ = call NFT.transferFromDip721(bob, alice, 0); let svg = stringify(title, "_nft_transfer.svg"); - output(file, stringify("[", __cost__, "](", svg, ")|\n")); + output(file, stringify("[", __cost__, "](", svg, ")|")); flamegraph(NFT, "NFT.transferFromDip721", svg); + // upgrade + upgrade(NFT, wasm, init); + let svg = stringify(title, "_upgrade.svg"); + flamegraph(NFT, "NFT.upgrade", svg); + output(file, stringify("[", _, "](", svg, ")|\n")); }; perf(motoko, "Motoko"); diff --git a/dapps/rust/basic_dao/Cargo.toml b/dapps/rust/basic_dao/Cargo.toml index 61727fc7..fa540e47 100644 --- a/dapps/rust/basic_dao/Cargo.toml +++ b/dapps/rust/basic_dao/Cargo.toml @@ -12,3 +12,4 @@ crate-type = ["cdylib"] ic-cdk.workspace = true serde.workspace = true candid.workspace = true +utils = { path = "../../../utils/rust" } diff --git a/dapps/rust/basic_dao/src/heartbeat.rs b/dapps/rust/basic_dao/src/heartbeat.rs index 06722702..644ce30c 100644 --- a/dapps/rust/basic_dao/src/heartbeat.rs +++ b/dapps/rust/basic_dao/src/heartbeat.rs @@ -1,3 +1,4 @@ +#![allow(dead_code)] //use ic_cdk::heartbeat; use crate::types::{Proposal, ProposalState}; use crate::SERVICE; diff --git a/dapps/rust/basic_dao/src/init.rs b/dapps/rust/basic_dao/src/init.rs deleted file mode 100644 index 7e7022dd..00000000 --- a/dapps/rust/basic_dao/src/init.rs +++ /dev/null @@ -1,15 +0,0 @@ -use crate::env::CanisterEnvironment; -use crate::service::BasicDaoService; -use crate::types::BasicDaoStableStorage; -use crate::SERVICE; -use ic_cdk::init; - -#[init] -fn init(init_state: BasicDaoStableStorage) { - ic_cdk::setup(); - - let mut init_service = BasicDaoService::from(init_state); - init_service.env = Box::new(CanisterEnvironment {}); - - SERVICE.with(|service| *service.borrow_mut() = init_service); -} diff --git a/dapps/rust/basic_dao/src/lib.rs b/dapps/rust/basic_dao/src/lib.rs index 4ddf2225..70c1d8e3 100644 --- a/dapps/rust/basic_dao/src/lib.rs +++ b/dapps/rust/basic_dao/src/lib.rs @@ -1,18 +1,55 @@ mod env; mod heartbeat; -mod init; mod service; mod types; -use crate::service::BasicDaoService; +use crate::env::CanisterEnvironment; +use crate::service::{BasicDaoService, StableState}; use crate::types::*; -use ic_cdk::{query, update}; +use ic_cdk::{init, post_upgrade, pre_upgrade, query, update}; use std::cell::RefCell; thread_local! { static SERVICE: RefCell = RefCell::default(); } +#[init] +fn init(init_state: BasicDaoStableStorage) { + ic_cdk::setup(); + utils::profiling_init(); + let mut init_service = BasicDaoService::from(init_state); + init_service.env = Box::new(CanisterEnvironment {}); + + SERVICE.with(|service| *service.borrow_mut() = init_service); +} + +#[pre_upgrade] +fn pre_upgrade() { + SERVICE.with(|serv| { + let serv = serv.borrow(); + let v = StableState { + accounts: serv.accounts.clone(), + proposals: serv.proposals.clone(), + next_proposal_id: serv.next_proposal_id, + system_params: serv.system_params.clone(), + }; + utils::save_stable(&v); + }); +} +#[post_upgrade] +fn post_upgrade() { + let v: StableState = utils::restore_stable(); + SERVICE.with(|cell| { + *cell.borrow_mut() = BasicDaoService { + env: Box::new(CanisterEnvironment {}), + accounts: v.accounts, + proposals: v.proposals, + next_proposal_id: v.next_proposal_id, + system_params: v.system_params, + } + }); +} + #[query] fn get_system_params() -> SystemParams { SERVICE.with(|service| service.borrow().system_params.clone()) diff --git a/dapps/rust/basic_dao/src/service.rs b/dapps/rust/basic_dao/src/service.rs index e5a4f616..ac9bcdd2 100644 --- a/dapps/rust/basic_dao/src/service.rs +++ b/dapps/rust/basic_dao/src/service.rs @@ -1,6 +1,6 @@ use crate::env::{EmptyEnvironment, Environment}; use crate::types::*; -use candid::Principal; +use candid::{CandidType, Deserialize, Principal}; use std::collections::HashMap; /// Implements the Basic DAO interface @@ -11,6 +11,13 @@ pub struct BasicDaoService { pub next_proposal_id: u64, pub system_params: SystemParams, } +#[derive(CandidType, Deserialize)] +pub struct StableState { + pub accounts: HashMap, + pub proposals: HashMap, + pub next_proposal_id: u64, + pub system_params: SystemParams, +} impl Default for BasicDaoService { fn default() -> Self { @@ -56,7 +63,7 @@ impl BasicDaoService { let caller = self.env.caller(); if let Some(account) = self.accounts.get_mut(&caller) { - if account.clone() < transfer.amount { + if *account < transfer.amount { return Err(format!( "Caller's account has insufficient funds to transfer {:?}", transfer.amount @@ -76,10 +83,7 @@ impl BasicDaoService { /// Return the account balance of the caller pub fn account_balance(&self) -> Tokens { let caller = self.env.caller(); - self.accounts - .get(&caller) - .cloned() - .unwrap_or_else(|| Default::default()) + self.accounts.get(&caller).cloned().unwrap_or_default() } /// Lists all accounts @@ -143,11 +147,10 @@ impl BasicDaoService { )); } - let voting_tokens = self + let voting_tokens = *self .accounts .get(&caller) - .ok_or_else(|| "Caller does not have any tokens to vote with".to_string())? - .clone(); + .ok_or_else(|| "Caller does not have any tokens to vote with".to_string())?; if proposal.voters.contains(&self.env.caller()) { return Err("Already voted".to_string()); @@ -163,7 +166,7 @@ impl BasicDaoService { if proposal.votes_yes >= self.system_params.proposal_vote_threshold { // Refund the proposal deposit when the proposal is accepted if let Some(account) = self.accounts.get_mut(&proposal.proposer) { - *account += self.system_params.proposal_submission_deposit.clone(); + *account += self.system_params.proposal_submission_deposit; } proposal.state = ProposalState::Accepted; @@ -208,13 +211,13 @@ impl BasicDaoService { fn deduct_proposal_submission_deposit(&mut self) -> Result<(), String> { let caller = self.env.caller(); if let Some(account) = self.accounts.get_mut(&caller) { - if account.clone() < self.system_params.proposal_submission_deposit { + if *account < self.system_params.proposal_submission_deposit { return Err(format!( "Caller's account must have at least {:?} to submit a proposal", self.system_params.proposal_submission_deposit )); } else { - *account -= self.system_params.proposal_submission_deposit.clone(); + *account -= self.system_params.proposal_submission_deposit; } } else { return Err("Caller needs an account to submit a proposal".to_string()); diff --git a/dapps/rust/dip721-nft/Cargo.toml b/dapps/rust/dip721-nft/Cargo.toml index 79ad5943..c652bf64 100644 --- a/dapps/rust/dip721-nft/Cargo.toml +++ b/dapps/rust/dip721-nft/Cargo.toml @@ -7,6 +7,7 @@ edition = "2018" ic-cdk.workspace = true candid.workspace = true serde.workspace = true +utils = { path = "../../../utils/rust" } [lib] crate-type = ["cdylib"] diff --git a/dapps/rust/dip721-nft/dip721-nft.did b/dapps/rust/dip721-nft/dip721-nft.did index 3e0f9b7c..c9d4b5fd 100644 --- a/dapps/rust/dip721-nft/dip721-nft.did +++ b/dapps/rust/dip721-nft/dip721-nft.did @@ -102,7 +102,7 @@ type BurnRequest = record { type InitArgs = record { custodians : opt vec principal; - logo : opt LogoResult; + logo : LogoResult; name : text; symbol : text; }; diff --git a/dapps/rust/dip721-nft/src/lib.rs b/dapps/rust/dip721-nft/src/lib.rs index e9318d70..c8934048 100644 --- a/dapps/rust/dip721-nft/src/lib.rs +++ b/dapps/rust/dip721-nft/src/lib.rs @@ -5,14 +5,13 @@ use std::cell::RefCell; use std::collections::{HashMap, HashSet}; use std::convert::TryFrom; use std::iter::FromIterator; -use std::mem; use std::num::TryFromIntError; use std::result::Result as StdResult; use candid::{CandidType, Deserialize, Encode, Principal}; use ic_cdk::{ api::{self, call}, - init, post_upgrade, pre_upgrade, query, storage, update, + init, post_upgrade, pre_upgrade, query, update, }; const MGMT: Principal = Principal::from_slice(&[]); @@ -21,21 +20,16 @@ thread_local! { static STATE: RefCell = RefCell::default(); } -#[derive(CandidType, Deserialize)] -struct StableState { - state: State, -} - #[pre_upgrade] fn pre_upgrade() { - let state = STATE.with(|state| mem::take(&mut *state.borrow_mut())); - let stable_state = StableState { state }; - storage::stable_save((stable_state,)).unwrap(); + STATE.with(|state| { + utils::save_stable(&state); + }); } #[post_upgrade] fn post_upgrade() { - let (StableState { state },) = storage::stable_restore().unwrap(); - STATE.with(|state0| *state0.borrow_mut() = state); + let value: State = utils::restore_stable(); + STATE.with(|cell| *cell.borrow_mut() = value); } #[derive(CandidType, Deserialize)] @@ -48,6 +42,7 @@ struct InitArgs { #[init] fn init(args: InitArgs) { + utils::profiling_init(); STATE.with(|state| { let mut state = state.borrow_mut(); state.custodians = args @@ -247,7 +242,7 @@ fn transfer_from_notify(from: Principal, to: Principal, token_id: u64, data: Vec // That means the original transfer must reply before that happens, or the caller will be // convinced that the transfer failed when it actually succeeded. So we don't await the call, // so that we'll reply immediately regardless of how long the notification call takes. - let _ = api::call::call_raw(to, "onDIP721Received", &arg, 0); + std::mem::drop(api::call::call_raw(to, "onDIP721Received", &arg, 0)); } Ok(res) } diff --git a/heartbeat/Makefile b/heartbeat/Makefile index fbdb7ff8..58d91852 100644 --- a/heartbeat/Makefile +++ b/heartbeat/Makefile @@ -1,32 +1,15 @@ -APP = heartbeat +include ../*.mk .PHONY: all motoko rust build perf all: build perf motoko: - cd motoko; \ - envsubst < mops.template.toml > mops.toml; \ - mops install; \ - dfx canister create --all; \ - dfx build; \ - echo "Optimize with ic-wasm level 3"; \ - for f in .dfx/local/canisters/*/*.wasm; do ic-wasm -o $$f $$f shrink --optimize O3 --keep-name-section; done; \ - rm mops.toml; \ - cd .. + $(call build_with_mops,motoko) rust: - cd rust; \ - dfx canister create --all; \ - dfx build; \ - echo "Optimize with ic-wasm level 3"; \ - for f in .dfx/local/canisters/*/*.wasm; do ic-wasm -o $$f $$f shrink --optimize O3 --keep-name-section; done; \ - cd .. + $(call build,rust) build: motoko rust perf: - set -e; \ - mkdir -p ../_out/$(APP); \ - cp README.md ../_out/$(APP); \ - cd ../_out/$(APP); \ - ic-repl ../../$(APP)/perf.sh + $(call perf,heartbeat,perf.sh) diff --git a/motoko/Makefile b/motoko/Makefile index e3a98ef9..1d5e0ebf 100644 --- a/motoko/Makefile +++ b/motoko/Makefile @@ -1,25 +1,10 @@ -APP = motoko +include ../*.mk .PHONY: all build perf all: build perf build: - envsubst < mops.template.toml > mops.toml; \ - mops install; \ - dfx canister create --all; \ - dfx build; \ - echo "Optimize with ic-wasm level 3"; \ - for f in .dfx/local/canisters/*/*.wasm; do ic-wasm -o $$f $$f shrink --optimize O3 --keep-name-section; done; \ - rm mops.toml; \ - cd .. + $(call build_with_mops,motoko) perf: - set -e; \ - mkdir -p ../_out/$(APP); \ - cp README.md ../_out/$(APP); \ - cd ../_out/$(APP); \ - ic-repl ../../$(APP)/gc.sh; \ - ic-repl ../../$(APP)/classes.sh - -clean: - rm *.wasm && rm mops.toml + $(call perf_two,motoko,gc.sh,classes.sh) diff --git a/motoko/classes.sh b/motoko/classes.sh index e85beed0..124310a6 100644 --- a/motoko/classes.sh +++ b/motoko/classes.sh @@ -1,7 +1,7 @@ #!ic-repl load "../prelude.sh"; -let class = wasm_profiling(".dfx/local/canisters/classes/classes.wasm"); +let class = wasm_profiling("motoko/.dfx/local/canisters/classes/classes.wasm"); let map = install(class, encode (), null); let file = "README.md"; diff --git a/motoko/dfx.json b/motoko/dfx.json deleted file mode 100644 index b18e8da9..00000000 --- a/motoko/dfx.json +++ /dev/null @@ -1,54 +0,0 @@ -{ - "canisters": { - "classes": { - "type": "motoko", - "main": "src/Map.mo" - }, - "default": { - "type": "custom", - "candid": "../collections/rust/collection.did", - "wasm": "default.wasm", - "build": [ - "sh -c '$(dfx cache show)/moc ../collections/motoko/src/triemap.mo -o default.wasm --public-metadata candid:service $(mops sources)'" - ] - }, - "copying": { - "type": "custom", - "candid": "../collections/rust/collection.did", - "wasm": "copying.wasm", - "build": [ - "sh -c '$(dfx cache show)/moc ../collections/motoko/src/triemap.mo -o copying.wasm --public-metadata candid:service $(mops sources) --force-gc --copying-gc'" - ] - }, - "compacting": { - "type": "custom", - "candid": "../collections/rust/collection.did", - "wasm": "compacting.wasm", - "build": [ - "sh -c '$(dfx cache show)/moc ../collections/motoko/src/triemap.mo -o compacting.wasm --public-metadata candid:service $(mops sources) --force-gc --compacting-gc'" - ] - }, - "generational": { - "type": "custom", - "candid": "../collections/rust/collection.did", - "wasm": "generational.wasm", - "build": [ - "sh -c '$(dfx cache show)/moc ../collections/motoko/src/triemap.mo -o generational.wasm --public-metadata candid:service $(mops sources) --force-gc --generational-gc'" - ] - }, - "incremental": { - "type": "custom", - "candid": "../collections/rust/collection.did", - "wasm": "incremental.wasm", - "build": [ - "sh -c '$(dfx cache show)/moc ../collections/motoko/src/triemap.mo -o incremental.wasm --public-metadata candid:service $(mops sources) --force-gc --incremental-gc'" - ] - } - }, - "defaults": { - "build": { - "packtool": "mops sources", - "args": "" - } - } -} diff --git a/motoko/gc.sh b/motoko/gc.sh index e8822ffe..14dfee0f 100644 --- a/motoko/gc.sh +++ b/motoko/gc.sh @@ -1,14 +1,15 @@ #!ic-repl load "../prelude.sh"; -let default = wasm_profiling("default.wasm", vec{"schedule_copying_gc"}); -let copying = wasm_profiling("copying.wasm", vec {"copying_gc"}); -let compacting = wasm_profiling("compacting.wasm", vec{"compacting_gc"}); -let generational = wasm_profiling("generational.wasm", vec{"generational_gc"}); -let incremental = wasm_profiling("incremental.wasm", vec{"incremental_gc"}); +let mo_config = record { start_page = 16; page_limit = 128 }; +let default = wasm_profiling("motoko/default.wasm", concat(mo_config, record { trace_only_funcs = vec{"schedule_copying_gc"} })); +let copying = wasm_profiling("motoko/copying.wasm", concat(mo_config, record { trace_only_funcs = vec {"copying_gc"} })); +let compacting = wasm_profiling("motoko/compacting.wasm", concat(mo_config, record { trace_only_funcs = vec{"compacting_gc"} })); +let generational = wasm_profiling("motoko/generational.wasm", concat(mo_config, record { trace_only_funcs = vec{"generational_gc"} })); +let incremental = wasm_profiling("motoko/incremental.wasm", concat(mo_config, record { trace_only_funcs = vec{"incremental_gc"} })); let file = "README.md"; -output(file, "\n\n## Garbage Collection\n\n| |generate 800k|max mem|batch_get 50|batch_put 50|batch_remove 50|\n|--:|--:|--:|--:|--:|--:|\n"); +output(file, "\n\n## Garbage Collection\n\n| |generate 700k|max mem|batch_get 50|batch_put 50|batch_remove 50|\n|--:|--:|--:|--:|--:|--:|\n"); function perf_mo(wasm, title, init) { let cid = install(wasm, encode (), null); @@ -40,7 +41,7 @@ function perf_mo(wasm, title, init) { uninstall(cid); }; -let init_size = 800_000; +let init_size = 700_000; perf_mo(default, "default", init_size); perf_mo(copying, "copying", init_size); perf_mo(compacting, "compacting", init_size); diff --git a/motoko/motoko/dfx.json b/motoko/motoko/dfx.json new file mode 100644 index 00000000..b47d608b --- /dev/null +++ b/motoko/motoko/dfx.json @@ -0,0 +1,54 @@ +{ + "canisters": { + "classes": { + "type": "motoko", + "main": "src/Map.mo" + }, + "default": { + "type": "custom", + "candid": "../../collections/rust/collection.did", + "wasm": "default.wasm", + "build": [ + "sh -c '$(dfx cache show)/moc ../../collections/motoko/src/triemap.mo -o default.wasm --public-metadata candid:service $(mops sources)'" + ] + }, + "copying": { + "type": "custom", + "candid": "../../collections/rust/collection.did", + "wasm": "copying.wasm", + "build": [ + "sh -c '$(dfx cache show)/moc ../../collections/motoko/src/triemap.mo -o copying.wasm --public-metadata candid:service $(mops sources) --force-gc --copying-gc'" + ] + }, + "compacting": { + "type": "custom", + "candid": "../../collections/rust/collection.did", + "wasm": "compacting.wasm", + "build": [ + "sh -c '$(dfx cache show)/moc ../../collections/motoko/src/triemap.mo -o compacting.wasm --public-metadata candid:service $(mops sources) --force-gc --compacting-gc'" + ] + }, + "generational": { + "type": "custom", + "candid": "../../collections/rust/collection.did", + "wasm": "generational.wasm", + "build": [ + "sh -c '$(dfx cache show)/moc ../../collections/motoko/src/triemap.mo -o generational.wasm --public-metadata candid:service $(mops sources) --force-gc --generational-gc'" + ] + }, + "incremental": { + "type": "custom", + "candid": "../../collections/rust/collection.did", + "wasm": "incremental.wasm", + "build": [ + "sh -c '$(dfx cache show)/moc ../../collections/motoko/src/triemap.mo -o incremental.wasm --public-metadata candid:service $(mops sources) --force-gc --incremental-gc'" + ] + } + }, + "defaults": { + "build": { + "packtool": "mops sources", + "args": "" + } + } +} diff --git a/motoko/mops.template.toml b/motoko/motoko/mops.template.toml similarity index 100% rename from motoko/mops.template.toml rename to motoko/motoko/mops.template.toml diff --git a/motoko/src/Buckets.mo b/motoko/motoko/src/Buckets.mo similarity index 100% rename from motoko/src/Buckets.mo rename to motoko/motoko/src/Buckets.mo diff --git a/motoko/src/Map.mo b/motoko/motoko/src/Map.mo similarity index 100% rename from motoko/src/Map.mo rename to motoko/motoko/src/Map.mo diff --git a/prelude.sh b/prelude.sh index 58e10401..7f0f880d 100644 --- a/prelude.sh +++ b/prelude.sh @@ -33,3 +33,5 @@ function get_memory(cid) { _.memory_size }; +let mo_config = record { start_page = 16; page_limit = 4096 }; +let rs_config = record { start_page = 1; page_limit = 4096 }; diff --git a/pub-sub/Makefile b/pub-sub/Makefile index 5133bd18..11adbb74 100644 --- a/pub-sub/Makefile +++ b/pub-sub/Makefile @@ -1,30 +1,15 @@ -APP = pub-sub +include ../*.mk .PHONY: all motoko rust build perf all: build perf motoko: - cd motoko; \ - dfx canister create --all; \ - dfx build; \ - echo "Optimize with ic-wasm level 3"; \ - for f in .dfx/local/canisters/*/*.wasm; do ic-wasm -o $$f $$f shrink --optimize O3 --keep-name-section; done; \ - cd .. + $(call build_with_mops,motoko) rust: - cd rust; \ - dfx canister create --all; \ - dfx build; \ - echo "Optimize with ic-wasm level 3"; \ - for f in .dfx/local/canisters/*/*.wasm; do ic-wasm -o $$f $$f shrink --optimize O3 --keep-name-section; done; \ - cd .. + $(call build,rust) build: motoko rust perf: - set -e; \ - mkdir -p ../_out/$(APP); \ - cp README.md ../_out/$(APP); \ - cd ../_out/$(APP); \ - ic-repl ../../$(APP)/motoko/perf.sh; \ - ic-repl ../../$(APP)/rust/perf.sh + $(call perf_two,pub-sub,motoko/perf.sh,rust/perf.sh) diff --git a/pub-sub/motoko/dfx.json b/pub-sub/motoko/dfx.json index fbccd05a..43bef3b6 100644 --- a/pub-sub/motoko/dfx.json +++ b/pub-sub/motoko/dfx.json @@ -6,5 +6,11 @@ "sub": { "main": "src/sub/Main.mo" } + }, + "defaults": { + "build": { + "packtool": "mops sources", + "args": "" + } } -} \ No newline at end of file +} diff --git a/pub-sub/motoko/mops.template.toml b/pub-sub/motoko/mops.template.toml new file mode 100644 index 00000000..976184ae --- /dev/null +++ b/pub-sub/motoko/mops.template.toml @@ -0,0 +1,2 @@ +[dependencies] +base = "https://github.com/dfinity/motoko-base#moc-$MOC_VERSION" diff --git a/pub-sub/rust/publisher/src/lib.rs b/pub-sub/rust/publisher/src/lib.rs index a649a0f1..f800d092 100644 --- a/pub-sub/rust/publisher/src/lib.rs +++ b/pub-sub/rust/publisher/src/lib.rs @@ -1,5 +1,5 @@ use candid::{CandidType, Principal}; -use ic_cdk::{query, update}; +use ic_cdk::update; use serde::Deserialize; use std::cell::RefCell; use std::collections::BTreeMap; diff --git a/utils.mk b/utils.mk new file mode 100644 index 00000000..2f55440c --- /dev/null +++ b/utils.mk @@ -0,0 +1,48 @@ +define ic-wasm + echo "Optimize with ic-wasm"; \ + for f in $(1)/*/*.wasm; do ic-wasm -o $$f $$f optimize O3 --keep-name-section; done +endef + +define build_with_mops + set -e; \ + cd $(1); \ + envsubst < mops.template.toml > mops.toml; \ + mops install; \ + dfx canister create --all; \ + dfx ledger fabricate-cycles --t 100 --canister $$(dfx identity get-wallet); \ + dfx build; \ + rm mops.toml; \ + $(call ic-wasm,.dfx/local/canisters/); \ + cd .. +endef + +define build + set -e; \ + cd $(1); \ + dfx canister create --all; \ + dfx ledger fabricate-cycles --t 100 --canister $$(dfx identity get-wallet); \ + dfx build; \ + $(call ic-wasm,.dfx/local/canisters/); \ + cd .. +endef + +define prepare_perf + mkdir -p ../_out/$(1); \ + cp README.md ../_out/$(1); \ + cd ../_out/$(1) +endef + +define perf + set -e; \ + $(call prepare_perf,$(1)); \ + ic-repl ../../$(1)/$(2); \ + du -h ../../_out +endef + +define perf_two + set -e; \ + $(call prepare_perf,$(1)); \ + ic-repl ../../$(1)/$(2); \ + ic-repl ../../$(1)/$(3); \ + du -h ../../_out +endef diff --git a/utils/motoko/Profiling.mo b/utils/motoko/Profiling.mo new file mode 100644 index 00000000..7f91dcad --- /dev/null +++ b/utils/motoko/Profiling.mo @@ -0,0 +1,9 @@ +import Region "mo:base/Region"; + +module { + public func init() : Region.Region { + let profiling = Region.new(); + ignore Region.grow(profiling, 4096); + profiling; + }; +} diff --git a/utils/rust/Cargo.toml b/utils/rust/Cargo.toml new file mode 100644 index 00000000..1d358a8b --- /dev/null +++ b/utils/rust/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "utils" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +candid.workspace = true +ic-stable-structures.workspace = true + diff --git a/utils/rust/src/lib.rs b/utils/rust/src/lib.rs new file mode 100644 index 00000000..08fac1f9 --- /dev/null +++ b/utils/rust/src/lib.rs @@ -0,0 +1,86 @@ +use candid::{CandidType, Decode, Deserialize, Encode}; +use ic_stable_structures::{ + memory_manager::{MemoryId, MemoryManager, VirtualMemory}, + writer::Writer, + DefaultMemoryImpl, Memory as _, +}; +use std::cell::RefCell; + +pub struct Random { + pub state: u64, + pub size: Option, + pub ind: u32, +} +impl Random { + pub fn new(size: Option, seed: u64) -> Self { + Random { + state: seed, + size, + ind: 0, + } + } +} +impl Iterator for Random { + type Item = u64; + fn next(&mut self) -> Option { + if let Some(size) = self.size { + self.ind += 1; + if self.ind > size { + return None; + } + } + self.state = self.state * 48271 % 0x7fffffff; + Some(self.state) + } +} +#[cfg(target_family = "wasm")] +pub fn get_mem() -> (u128, u128, u128) { + let size = core::arch::wasm32::memory_size(0) as u128 * 65536; + (size, size, size) +} +#[cfg(not(target_family = "wasm"))] +pub fn get_mem() -> (u128, u128, u128) { + unimplemented!() +} +pub fn get_upgrade_mem_size() -> (u128, u128, u128) { + let memory = get_upgrade_memory(); + let size = memory.size() as u128 * 65536; + (size, size, size) +} + +thread_local! { + static MEMORY_MANAGER: RefCell> = + RefCell::new(MemoryManager::init(DefaultMemoryImpl::default())); +} + +const PROFILING: MemoryId = MemoryId::new(100); +const UPGRADES: MemoryId = MemoryId::new(0); + +pub type Memory = VirtualMemory; + +pub fn get_upgrade_memory() -> Memory { + MEMORY_MANAGER.with(|m| m.borrow().get(UPGRADES)) +} + +pub fn profiling_init() { + let memory = MEMORY_MANAGER.with(|m| m.borrow().get(PROFILING)); + memory.grow(4096); +} + +pub fn save_stable(val: &T) { + let bytes = Encode!(val).unwrap(); + let len = bytes.len() as u32; + let mut memory = MEMORY_MANAGER.with(|m| m.borrow().get(UPGRADES)); + let mut writer = Writer::new(&mut memory, 0); + writer.write(&len.to_le_bytes()).unwrap(); + writer.write(&bytes).unwrap(); +} +pub fn restore_stable Deserialize<'a>>() -> T { + let memory = MEMORY_MANAGER.with(|m| m.borrow().get(UPGRADES)); + let mut len_bytes = [0; 4]; + memory.read(0, &mut len_bytes); + let len = u32::from_le_bytes(len_bytes) as usize; + let mut bytes = vec![0; len]; + memory.read(4, &mut bytes); + Decode!(&bytes, T).unwrap() +}