Skip to content

Commit

Permalink
profiling upgrades (#91)
Browse files Browse the repository at this point in the history
* upgrade for basic_dao

* nft

* rerun CI

* collection motoko

* rust collections

* fix

* fix

* add utils

* use utils in collections

* refactor

* fix

* certified_map upgrade

* readme

* fix nft did

* fix
  • Loading branch information
chenyan-dfinity authored Sep 28, 2023
1 parent 5158daf commit 9c6c2f2
Show file tree
Hide file tree
Showing 48 changed files with 486 additions and 241 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/perf.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ jobs:
fail-fast: false
env:
DFX_VERSION: 0.15.0
IC_REPL_VERSION: 0.4.3
IC_REPL_VERSION: 0.5.0
MOC_VERSION: 0.10.0
IC_WASM_VERSION: 0.5.0
IC_WASM_VERSION: 0.5.1
steps:
- uses: actions/checkout@v3
- name: Checkout out gh-pages report
Expand Down
27 changes: 25 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
[workspace]
members = [
"utils/rust",
"collections/rust/src/hashmap",
"collections/rust/src/btreemap",
"collections/rust/src/heap",
Expand All @@ -24,5 +25,6 @@ opt-level = 2
[workspace.dependencies]
ic-cdk = "0.10.0"
ic-cdk-timers = "0.4.0"
candid = "0.9"
candid = "0.9.8"
serde = "1"
ic-stable-structures = "0.5"
8 changes: 7 additions & 1 deletion collections/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,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. The map state is persisted by serializing and deserializing states into stable memory.

## **💎 Takeaways**

Expand All @@ -22,7 +23,12 @@ 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`
> * `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).
Expand Down
3 changes: 3 additions & 0 deletions collections/motoko/src/ZhenyaHashmap.mo
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@ 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<Nat64> = (f_hash, Nat64.equal);
stable var map = HashMap.new<Nat64, Nat64>();
Expand Down
3 changes: 3 additions & 0 deletions collections/motoko/src/btreemap.mo
Original file line number Diff line number Diff line change
Expand Up @@ -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<Nat64, Nat64>(null);
let rand = Random.new(null, 42);

Expand Down
11 changes: 11 additions & 0 deletions collections/motoko/src/buffer.mo
Original file line number Diff line number Diff line change
Expand Up @@ -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<Nat64>(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();
Expand Down
11 changes: 11 additions & 0 deletions collections/motoko/src/hashmap.mo
Original file line number Diff line number Diff line change
Expand Up @@ -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<Nat64, Nat64>(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<Nat64, (Nat64, Nat64)>(rand, func x = (x, x));
Expand Down
11 changes: 11 additions & 0 deletions collections/motoko/src/heap.mo
Original file line number Diff line number Diff line change
Expand Up @@ -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>(Nat64.compare);
stable var stableMap : Heap.Tree<Nat64> = 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);
Expand Down
11 changes: 11 additions & 0 deletions collections/motoko/src/rbtree.mo
Original file line number Diff line number Diff line change
Expand Up @@ -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, Nat64>(Nat64.compare);
stable var stableMap : RBTree.Tree<Nat64, Nat64> = #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<Nat64, (Nat64, Nat64)>(rand, func x = (x, x));
Expand Down
11 changes: 11 additions & 0 deletions collections/motoko/src/splay.mo
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
11 changes: 11 additions & 0 deletions collections/motoko/src/triemap.mo
Original file line number Diff line number Diff line change
Expand Up @@ -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, Nat64>(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<Nat64, (Nat64, Nat64)>(rand, func x = (x, x));
Expand Down
19 changes: 11 additions & 8 deletions collections/motoko/src/vector.mo
Original file line number Diff line number Diff line change
@@ -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<Nat64>();
stable let profiling = Profiling.init();

stable let buffer : Vector.Vector<Nat64> = Vector.new();

public func generate(n: Nat) : async () {
buffer.clear();
Vector.clear(buffer);
for (_ in Iter.range(1, n)) {
buffer.add(42);
Vector.add<Nat64>(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<Nat64>(buffer, 42);
}
};
public func batch_remove(n : Nat) : async () {
for (_ in Iter.range(1, n)) {
ignore buffer.removeLast();
ignore Vector.removeLast(buffer);
}
};
}
Loading

0 comments on commit 9c6c2f2

Please sign in to comment.