From d62ec8a522f92d0a0ccf226c6e929bb918b31128 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82?= <128649481+neotheprogramist@users.noreply.github.com> Date: Fri, 3 Nov 2023 20:59:24 +0100 Subject: [PATCH] Implement Comprehensive Dojo Storage Benchmarking (#1097) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * minimal benchmark setup * simple struct benches * native storage benchmarks * struct bench fix * benchmark anomaly fix and simple struct bench * complex struct serialization benchmark * character struct without nesting * nested struct test and issue * passing character benchmark * enum and tuple in characte * big index and db set benches * assertion that file haven't changed * long struct bench --------- Co-authored-by: Mateusz Zając --- benches.txt | 111 +++++- crates/dojo-core/src/benchmarks.cairo | 482 +++++++++++++++++++++++--- crates/dojo-core/src/test_utils.cairo | 35 +- crates/dojo-core/src/world_test.cairo | 94 ++++- crates/dojo-erc/Scarb.lock | 20 ++ scripts/cairo_bench.sh | 7 +- 6 files changed, 681 insertions(+), 68 deletions(-) create mode 100644 crates/dojo-erc/Scarb.lock diff --git a/benches.txt b/benches.txt index 99a8cec447..b1273abbdc 100644 --- a/benches.txt +++ b/benches.txt @@ -1,13 +1,100 @@ bench empty: 0 - idx create 1st: 80050 - idx dlt last: 112070 - idx dlt !last: 112070 - idx empty: 40460 - idx exists chk: 16010 - idx query 2nd: 80050 - idx query one: 80920 - idx query two: 121380 - storage get: 16010 -storage get mny: 268944 - storage set: 16010 -storage set mny: 263344 + bench empty: 0 + case db get: 754172 + case db get: 754172 + case db set: 338382 + case db set: 338382 + case init: 770 + case init: 770 + case serialize: 4610 + case serialize: 4610 + case values: 36120 + case values: 36120 + char get macro: 11009694 + char get macro: 11009694 + chars db get: 10136764 + chars db get: 10136764 + chars db set: 5028344 + chars db set: 5028344 + char set call: 6048284 + char set call: 6048284 + chars init: 770 + chars init: 770 +chars serialize: 18920 +chars serialize: 18920 + chars values: 22070 + chars values: 22070 + db del arr: 14240 + db del arr: 14240 + db get arr: 1026188 + db get arr: 1026188 +db get half arr: 725458 +db get half arr: 725458 + dbi scan arr 1: 452574 + dbi scan arr 1: 452574 + dbi scan arr 2: 804778 + dbi scan arr 2: 804778 +dbi set arr 1st: 482514 +dbi set arr 1st: 482514 +dbi set arr 2nd: 482334 +dbi set arr 2nd: 482334 + db set arr: 854708 + db set arr: 854708 + foo get macro: 1021934 + foo get macro: 1021934 + foo init: 770 + foo init: 770 + foo serialize: 2220 + foo serialize: 2220 + foo set call: 813314 + foo set call: 813314 + foo values: 12160 + foo values: 12160 +idx create 1000: 189333070 +idx create 1000: 189333070 + idx create 1st: 78280 + idx create 1st: 78280 +idx dlt !1000 0: 164350 +idx dlt !1000 0: 164350 + idx dlt 1000: 110300 + idx dlt 1000: 110300 +idx dlt !1000 >: 261360 +idx dlt !1000 >: 261360 + idx dlt last: 284480 + idx dlt last: 284480 + idx dlt !last: 285040 + idx dlt !last: 285040 + idx empty: 185890 + idx empty: 185890 +idx exists 1000: 14240 +idx exists 1000: 14240 + idx exists chk: 40520 + idx exists chk: 40520 + idx query 1000: 40716000 + idx query 1000: 40716000 + idx query 2nd: 187780 + idx query 2nd: 187780 + idx query one: 119120 + idx query one: 119120 + idx query two: 159580 + idx query two: 159580 + native prep: 11600 + native prep: 11600 + native prep of: 11600 + native prep of: 11600 + native read: 17880 + native read: 17880 + native read of: 17880 + native read of: 17880 + native write: 17260 + native write: 17260 + native writ of: 17260 + native writ of: 17260 + storage get: 36860 + storage get: 36860 +storage get mny: 297124 +storage get mny: 297124 + storage set: 36600 + storage set: 36600 +storage set mny: 283984 +storage set mny: 283984 diff --git a/crates/dojo-core/src/benchmarks.cairo b/crates/dojo-core/src/benchmarks.cairo index 7619d1866c..62f21918ef 100644 --- a/crates/dojo-core/src/benchmarks.cairo +++ b/crates/dojo-core/src/benchmarks.cairo @@ -3,45 +3,22 @@ use array::ArrayTrait; use array::SpanTrait; use debug::PrintTrait; use option::OptionTrait; +use poseidon::poseidon_hash_span; +use starknet::SyscallResultTrait; +use starknet::{contract_address_const, ContractAddress, ClassHash, get_caller_address}; use dojo::database; use dojo::database::{storage, index}; -use dojo::packing::{shl, shr}; +use dojo::model::Model; +use dojo::world_test::Foo; +use dojo::test_utils::end; -const GAS_OFFSET: felt252 = 0x1_000000_000000_000000_000000_000000; // 15 bajtów - -fn start() -> u128 { - let gas = testing::get_available_gas(); - gas::withdraw_gas().unwrap(); - gas -} - -fn end(start: u128, name: felt252) { - let gas_after = testing::get_available_gas(); - let mut name: u256 = name.into(); - - // overwriting zeros with spaces - let mut char = 0; - loop { - if char == 15 { - break; - } - // if given byte is zero - if shl(0xff, 8 * char) & name == 0 { - name = name | shl(0x20, 8 * char); // set space - } - char += 1; - }; - - let name: felt252 = (name % GAS_OFFSET.into()).try_into().unwrap(); - let used_gas = (start - gas_after).into() * GAS_OFFSET; - (used_gas + name).print(); -} #[test] #[available_gas(1000000000)] fn bench_reference_offset() { - let gas = start(); + let gas = testing::get_available_gas(); + gas::withdraw_gas().unwrap(); end(gas, 'bench empty'); } @@ -50,11 +27,13 @@ fn bench_reference_offset() { fn bench_storage_single() { let keys = array!['database_test', '42'].span(); - let gas = start(); + let gas = testing::get_available_gas(); + gas::withdraw_gas().unwrap(); storage::set(0, keys, 420); end(gas, 'storage set'); - let gas = start(); + let gas = testing::get_available_gas(); + gas::withdraw_gas().unwrap(); let res = storage::get(0, keys); end(gas, 'storage get'); @@ -68,11 +47,13 @@ fn bench_storage_many() { let values = array![1, 2].span(); let layout = array![251, 251].span(); - let gas = start(); + let gas = testing::get_available_gas(); + gas::withdraw_gas().unwrap(); storage::set_many(0, keys, 0, values, layout); end(gas, 'storage set mny'); - let gas = start(); + let gas = testing::get_available_gas(); + gas::withdraw_gas().unwrap(); let res = storage::get_many(0, keys, 0, 2, layout); end(gas, 'storage get mny'); @@ -81,51 +62,148 @@ fn bench_storage_many() { assert(*res.at(1) == *values.at(1), 'value not set'); } +#[test] +#[available_gas(1000000000)] +fn bench_native_storage() { + let gas = testing::get_available_gas(); + gas::withdraw_gas().unwrap(); + let keys = array![0x1337].span(); + let base = starknet::storage_base_address_from_felt252(poseidon_hash_span(keys)); + let address = starknet::storage_address_from_base(base); + end(gas, 'native prep'); + + let gas = testing::get_available_gas(); + gas::withdraw_gas().unwrap(); + starknet::storage_write_syscall(0, address, 42); + end(gas, 'native write'); + + let gas = testing::get_available_gas(); + gas::withdraw_gas().unwrap(); + let value = starknet::storage_read_syscall(0, address).unwrap_syscall(); + end(gas, 'native read'); + + assert(value == 42, 'read invalid'); +} + +#[test] +#[available_gas(1000000000)] +fn bench_native_storage_offset() { + let gas = testing::get_available_gas(); + gas::withdraw_gas().unwrap(); + let keys = array![0x1337].span(); + let base = starknet::storage_base_address_from_felt252(poseidon_hash_span(keys)); + let address = starknet::storage_address_from_base_and_offset(base, 42); + end(gas, 'native prep of'); + + let gas = testing::get_available_gas(); + gas::withdraw_gas().unwrap(); + starknet::storage_write_syscall(0, address, 42); + end(gas, 'native writ of'); + + let gas = testing::get_available_gas(); + gas::withdraw_gas().unwrap(); + let value = starknet::storage_read_syscall(0, address).unwrap_syscall(); + end(gas, 'native read of'); + + assert(value == 42, 'read invalid'); +} + #[test] #[available_gas(1000000000)] fn bench_index() { - let gas = start(); + let gas = testing::get_available_gas(); + gas::withdraw_gas().unwrap(); let no_query = index::query(0, 69, Option::None(())); end(gas, 'idx empty'); assert(no_query.len() == 0, 'entity indexed'); - let gas = start(); + let gas = testing::get_available_gas(); + gas::withdraw_gas().unwrap(); index::create(0, 69, 420); end(gas, 'idx create 1st'); - let gas = start(); + let gas = testing::get_available_gas(); + gas::withdraw_gas().unwrap(); let query = index::query(0, 69, Option::None(())); end(gas, 'idx query one'); assert(query.len() == 1, 'entity not indexed'); assert(*query.at(0) == 420, 'entity value incorrect'); - let gas = start(); + let gas = testing::get_available_gas(); + gas::withdraw_gas().unwrap(); index::create(0, 69, 1337); end(gas, 'idx query 2nd'); - let gas = start(); + let gas = testing::get_available_gas(); + gas::withdraw_gas().unwrap(); let two_query = index::query(0, 69, Option::None(())); end(gas, 'idx query two'); assert(two_query.len() == 2, 'index should have two query'); assert(*two_query.at(1) == 1337, 'entity value incorrect'); - let gas = start(); + let gas = testing::get_available_gas(); + gas::withdraw_gas().unwrap(); index::exists(0, 69, 420); end(gas, 'idx exists chk'); - let gas = start(); + let gas = testing::get_available_gas(); + gas::withdraw_gas().unwrap(); index::delete(0, 69, 420); end(gas, 'idx dlt !last'); assert(!index::exists(0, 69, 420), 'entity should not exist'); - let gas = start(); + let gas = testing::get_available_gas(); + gas::withdraw_gas().unwrap(); index::delete(0, 69, 1337); end(gas, 'idx dlt last'); assert(!index::exists(0, 69, 1337), 'entity should not exist'); } +#[test] +#[available_gas(1000000000)] +fn bench_big_index() { + let gas = testing::get_available_gas(); + gas::withdraw_gas().unwrap(); + let mut i = 0; + loop { + if i == 1000 { + break; + } + index::create(0, 69, i); + i += 1; + }; + end(gas, 'idx create 1000'); + + let gas = testing::get_available_gas(); + gas::withdraw_gas().unwrap(); + let query = index::query(0, 69, Option::None(())); + end(gas, 'idx query 1000'); + assert(query.len() == 1000, 'entity not indexed'); + assert(*query.at(420) == 420, 'entity value incorrect'); + + let gas = testing::get_available_gas(); + gas::withdraw_gas().unwrap(); + index::exists(0, 69, 999); + end(gas, 'idx exists 1000'); + + let gas = testing::get_available_gas(); + gas::withdraw_gas().unwrap(); + index::delete(0, 69, 999); + end(gas, 'idx dlt 1000'); + + let gas = testing::get_available_gas(); + gas::withdraw_gas().unwrap(); + index::delete(0, 69, 420); + end(gas, 'idx dlt !1000 >'); + + let gas = testing::get_available_gas(); + gas::withdraw_gas().unwrap(); + index::delete(0, 69, 420); + end(gas, 'idx dlt !1000 0'); +} + #[test] #[available_gas(1000000000)] fn bench_database_array() { @@ -134,11 +212,13 @@ fn bench_database_array() { let half_layout = array![251, 251, 251, 251, 251].span(); let len = value.len(); - let gas = start(); + let gas = testing::get_available_gas(); + gas::withdraw_gas().unwrap(); database::set('table', 'key', 0, value, layout); end(gas, 'db set arr'); - let gas = start(); + let gas = testing::get_available_gas(); + gas::withdraw_gas().unwrap(); let res = database::get('table', 'key', 0, len, layout); end(gas, 'db get arr'); @@ -146,14 +226,16 @@ fn bench_database_array() { assert(*res.at(0) == *value.at(0), 'value not set'); assert(*res.at(1) == *value.at(1), 'value not set'); - let gas = start(); + let gas = testing::get_available_gas(); + gas::withdraw_gas().unwrap(); let second_res = database::get('table', 'key', 3, 8, array![251, 251, 251, 251, 251].span()); end(gas, 'db get half arr'); assert(second_res.len() == 5, 'wrong number of values'); assert(*second_res.at(0) == *value.at(3), 'value not set'); - let gas = start(); + let gas = testing::get_available_gas(); + gas::withdraw_gas().unwrap(); database::del('table', 'key'); end(gas, 'db del arr'); } @@ -165,19 +247,23 @@ fn bench_indexed_database_array() { let odd = array![1, 3].span(); let layout = array![251, 251].span(); - let gas = start(); + let gas = testing::get_available_gas(); + gas::withdraw_gas().unwrap(); database::set_with_index('table', 'even', 0, even, layout); end(gas, 'dbi set arr 1st'); - let gas = start(); + let gas = testing::get_available_gas(); + gas::withdraw_gas().unwrap(); let (keys, values) = database::scan('table', Option::None(()), 2, layout); end(gas, 'dbi scan arr 1'); - let gas = start(); + let gas = testing::get_available_gas(); + gas::withdraw_gas().unwrap(); database::set_with_index('table', 'odd', 0, odd, layout); end(gas, 'dbi set arr 2nd'); - let gas = start(); + let gas = testing::get_available_gas(); + gas::withdraw_gas().unwrap(); let (keys, values) = database::scan('table', Option::None(()), 2, layout); end(gas, 'dbi scan arr 2'); @@ -187,3 +273,297 @@ fn bench_indexed_database_array() { assert(*(*values.at(0)).at(0) == 2, 'Wrong value at index 0!'); assert(*(*values.at(0)).at(1) == 4, 'Wrong value at index 1!'); } + + +#[test] +#[available_gas(1000000000)] +fn bench_simple_struct() { + let caller = starknet::contract_address_const::<0x42>(); + + let gas = testing::get_available_gas(); + gas::withdraw_gas().unwrap(); + let mut foo = Foo { + caller, + a: 0x123456789abcdef, + b: 0x123456789abcdef, + }; + end(gas, 'foo init'); + + let gas = testing::get_available_gas(); + gas::withdraw_gas().unwrap(); + let mut serialized = ArrayTrait::new(); + serde::Serde::serialize(@foo.a, ref serialized); + serde::Serde::serialize(@foo.b, ref serialized); + let serialized = array::ArrayTrait::span(@serialized); + end(gas, 'foo serialize'); + + let gas = testing::get_available_gas(); + gas::withdraw_gas().unwrap(); + let values: Span = foo.values(); + end(gas, 'foo values'); + + assert(serialized.len() == 2, 'serialized wrong length'); + assert(values.len() == 2, 'value wrong length'); + assert(serialized.at(0) == values.at(0), 'serialized differ at 0'); + assert(serialized.at(1) == values.at(1), 'serialized differ at 1'); +} + +#[derive(Model, Copy, Drop, Serde)] +struct PositionWithQuaterions { + #[key] + id: felt252, + x: felt252, + y: felt252, + z: felt252, + a: felt252, + b: felt252, + c: felt252, + d: felt252, +} + +#[test] +#[available_gas(1000000000)] +fn test_struct_with_many_fields() { + let gas = testing::get_available_gas(); + gas::withdraw_gas().unwrap(); + + let mut pos = PositionWithQuaterions { + id: 0x123456789abcdef, + x: 0x123456789abcdef, + y: 0x123456789abcdef, + z: 0x123456789abcdef, + a: 0x123456789abcdef, + b: 0x123456789abcdef, + c: 0x123456789abcdef, + d: 0x123456789abcdef, + }; + end(gas, 'pos init'); + + let gas = testing::get_available_gas(); + gas::withdraw_gas().unwrap(); + let mut serialized = ArrayTrait::new(); + serde::Serde::serialize(@pos.x, ref serialized); + serde::Serde::serialize(@pos.y, ref serialized); + serde::Serde::serialize(@pos.z, ref serialized); + serde::Serde::serialize(@pos.a, ref serialized); + serde::Serde::serialize(@pos.b, ref serialized); + serde::Serde::serialize(@pos.c, ref serialized); + serde::Serde::serialize(@pos.d, ref serialized); + let serialized = array::ArrayTrait::span(@serialized); + end(gas, 'pos serialize'); + + let gas = testing::get_available_gas(); + gas::withdraw_gas().unwrap(); + let values: Span = pos.values(); + end(gas, 'pos values'); + + assert(serialized.len() == values.len(), 'serialized not equal'); + let mut idx = 0; + loop { + if idx == serialized.len() { + break; + } + assert(serialized.at(idx) == values.at(idx), 'serialized differ'); + idx += 1; + }; + + let gas = testing::get_available_gas(); + gas::withdraw_gas().unwrap(); + database::set('positions', '42', 0, pos.values(), pos.layout()); + end(gas, 'pos db set'); + + let gas = testing::get_available_gas(); + gas::withdraw_gas().unwrap(); + database::get('positions', '42', 0, pos.packed_size(), pos.layout()); + end(gas, 'pos db get'); +} + + +#[derive(Introspect, Copy, Drop, Serde)] +struct Sword { + swordsmith: ContractAddress, + damage: u32, +} + +#[derive(Model, Copy, Drop, Serde)] +struct Case { + #[key] + owner: ContractAddress, + sword: Sword, + material: felt252, +} + + +#[test] +#[available_gas(1000000000)] +fn bench_nested_struct() { + let caller = starknet::contract_address_const::<0x42>(); + let gas = testing::get_available_gas(); + gas::withdraw_gas().unwrap(); + + let mut case = Case { + owner: caller, + sword: Sword { + swordsmith: caller, + damage: 0x12345678, + }, + material: 'wooden', + }; + end(gas, 'case init'); + let gas = testing::get_available_gas(); + gas::withdraw_gas().unwrap(); + + + let gas = testing::get_available_gas(); + gas::withdraw_gas().unwrap(); + let mut serialized = ArrayTrait::new(); + serde::Serde::serialize(@case.sword, ref serialized); + serde::Serde::serialize(@case.material, ref serialized); + let serialized = array::ArrayTrait::span(@serialized); + end(gas, 'case serialize'); + + let gas = testing::get_available_gas(); + gas::withdraw_gas().unwrap(); + let values: Span = case.values(); + end(gas, 'case values'); + + assert(serialized.len() == values.len(), 'serialized not equal'); + let mut idx = 0; + loop { + if idx == serialized.len() { + break; + } + assert(serialized.at(idx) == values.at(idx), 'serialized differ'); + idx += 1; + }; + + let gas = testing::get_available_gas(); + gas::withdraw_gas().unwrap(); + database::set('cases', '42', 0, case.values(), case.layout()); + end(gas, 'case db set'); + + let gas = testing::get_available_gas(); + gas::withdraw_gas().unwrap(); + database::get('cases', '42', 0, case.packed_size(), case.layout()); + end(gas, 'case db get'); +} + +#[derive(Model, Copy, Drop, Serde)] +struct Character { + #[key] + caller: ContractAddress, + heigth: felt252, + abilities: Abilities, + stats: Stats, + weapon: Weapon, + gold: u32, +} + +#[derive(Introspect, Copy, Drop, Serde)] +struct Abilities { + strength: u8, + dexterity: u8, + constitution: u8, + intelligence: u8, + wisdom: u8, + charisma: u8, +} + +#[derive(Introspect, Copy, Drop, Serde)] +struct Stats { + kills: u128, + deaths: u16, + rests: u32, + hits: u64, + blocks: u32, + walked: felt252, + runned: felt252, + finished: bool, + romances: u16, +} + +#[derive(Introspect, Copy, Drop, Serde)] +enum Weapon { + DualWield: (Sword, Sword), + Fists: (Sword, Sword), // Introspect requires same arms +} + +#[test] +#[available_gas(1000000000)] +fn bench_complex_struct() { + let gas = testing::get_available_gas(); + gas::withdraw_gas().unwrap(); + + let char = Character { + caller: starknet::contract_address_const::<0x42>(), + heigth: 0x123456789abcdef, + abilities: Abilities { + strength: 0x12, + dexterity: 0x34, + constitution: 0x56, + intelligence: 0x78, + wisdom: 0x9a, + charisma: 0xbc, + }, + stats: Stats { + kills: 0x123456789abcdef, + deaths: 0x1234, + rests: 0x12345678, + hits: 0x123456789abcdef, + blocks: 0x12345678, + walked: 0x123456789abcdef, + runned: 0x123456789abcdef, + finished: true, + romances: 0x1234, + }, + weapon: Weapon::DualWield(( + Sword { + swordsmith: starknet::contract_address_const::<0x69>(), + damage: 0x12345678, + }, + Sword { + swordsmith: starknet::contract_address_const::<0x69>(), + damage: 0x12345678, + } + )), + gold: 0x12345678, + }; + end(gas, 'chars init'); + + let gas = testing::get_available_gas(); + gas::withdraw_gas().unwrap(); + let mut serialized = ArrayTrait::new(); + serde::Serde::serialize(@char.heigth, ref serialized); + serde::Serde::serialize(@char.abilities, ref serialized); + serde::Serde::serialize(@char.stats, ref serialized); + serde::Serde::serialize(@char.weapon, ref serialized); + serde::Serde::serialize(@char.gold, ref serialized); + let serialized = array::ArrayTrait::span(@serialized); + end(gas, 'chars serialize'); + + let gas = testing::get_available_gas(); + gas::withdraw_gas().unwrap(); + let values: Span = char.values(); + end(gas, 'chars values'); + + assert(serialized.len() == values.len(), 'serialized not equal'); + + let mut idx = 0; + loop { + if idx == serialized.len() { + break; + } + assert(serialized.at(idx) == values.at(idx), 'serialized differ'); + idx += 1; + }; + + let gas = testing::get_available_gas(); + gas::withdraw_gas().unwrap(); + database::set('chars', '42', 0, char.values(), char.layout()); + end(gas, 'chars db set'); + + let gas = testing::get_available_gas(); + gas::withdraw_gas().unwrap(); + database::get('chars', '42', 0, char.packed_size(), char.layout()); + end(gas, 'chars db get'); +} \ No newline at end of file diff --git a/crates/dojo-core/src/test_utils.cairo b/crates/dojo-core/src/test_utils.cairo index 5d10875ec9..8cc2bec88d 100644 --- a/crates/dojo-core/src/test_utils.cairo +++ b/crates/dojo-core/src/test_utils.cairo @@ -6,10 +6,11 @@ use array::{ArrayTrait, SpanTrait}; use traits::TryInto; use option::OptionTrait; use core::{result::ResultTrait, traits::Into}; - +use debug::PrintTrait; use dojo::executor::executor; use dojo::world::{world, IWorldDispatcher, IWorldDispatcherTrait}; +use dojo::packing::{shl, shr}; /// Deploy classhash with calldata for constructor /// @@ -68,3 +69,35 @@ fn spawn_test_world(models: Array) -> IWorldDispatcher { world } + + +const GAS_OFFSET: felt252 = 0x1_000000_000000_000000_000000_000000; // 15 bajtów + +/// Measures gas used after previous measurement and prints it +/// +/// # Arguments +/// +/// * `start` - gas before measurement +/// * `name` - name of test, at most 15 bytes, will be padded with spaces +fn end(start: u128, name: felt252) { + let gas_after = testing::get_available_gas(); + gas::withdraw_gas().unwrap(); + let mut name: u256 = name.into(); + + // overwriting zeros with spaces + let mut char = 0; + loop { + if char == 15 { + break; + } + // if given byte is zero + if shl(0xff, 8 * char) & name == 0 { + name = name | shl(0x20, 8 * char); // set space + } + char += 1; + }; + + let name: felt252 = (name % GAS_OFFSET.into()).try_into().unwrap(); + let used_gas = (start - gas_after - 1770).into() * GAS_OFFSET; + (used_gas + name).print(); +} diff --git a/crates/dojo-core/src/world_test.cairo b/crates/dojo-core/src/world_test.cairo index 69869c0d63..36d260763b 100644 --- a/crates/dojo-core/src/world_test.cairo +++ b/crates/dojo-core/src/world_test.cairo @@ -7,10 +7,12 @@ use starknet::class_hash::Felt252TryIntoClassHash; use starknet::{contract_address_const, ContractAddress, ClassHash, get_caller_address}; use starknet::syscalls::deploy_syscall; +use dojo::benchmarks; use dojo::executor::executor; use dojo::world::{IWorldDispatcher, IWorldDispatcherTrait, world}; use dojo::database::schema::SchemaIntrospection; use dojo::test_utils::{spawn_test_world, deploy_with_world_address}; +use dojo::benchmarks::{Character, end}; #[derive(Model, Copy, Drop, Serde)] struct Foo { @@ -30,11 +32,13 @@ struct Fizz { #[starknet::interface] trait Ibar { fn set_foo(self: @TContractState, a: felt252, b: u128); + fn set_char(self: @TContractState, a: felt252, b: u32); } #[starknet::contract] mod bar { use super::{Foo, IWorldDispatcher, IWorldDispatcherTrait}; + use super::benchmarks::{Character, Abilities, Stats, Weapon, Sword}; use traits::Into; use starknet::{get_caller_address, ContractAddress}; @@ -52,6 +56,46 @@ mod bar { fn set_foo(self: @ContractState, a: felt252, b: u128) { set!(self.world.read(), Foo { caller: get_caller_address(), a, b }); } + + fn set_char(self: @ContractState, a: felt252, b: u32) { + set!( + self.world.read(), + Character { + caller: get_caller_address(), + heigth: a, + abilities: Abilities { + strength: 0x12, + dexterity: 0x34, + constitution: 0x56, + intelligence: 0x78, + wisdom: 0x9a, + charisma: 0xbc, + }, + stats: Stats { + kills: 0x123456789abcdef, + deaths: 0x1234, + rests: 0x12345678, + hits: 0x123456789abcdef, + blocks: 0x12345678, + walked: 0x123456789abcdef, + runned: 0x123456789abcdef, + finished: true, + romances: 0x1234, + }, + weapon: Weapon::DualWield(( + Sword { + swordsmith: get_caller_address(), + damage: 0x12345678, + }, + Sword { + swordsmith: get_caller_address(), + damage: 0x12345678, + } + )), + gold: b, + } + ); + } } } @@ -390,12 +434,56 @@ fn test_execute_multiple_worlds() { bar1_contract.set_foo(1337, 1337); bar2_contract.set_foo(7331, 7331); - let mut keys = ArrayTrait::new(); - keys.append(0); - let data1 = get!(world1, alice, Foo); let data2 = get!(world2, alice, Foo); assert(data1.a == 1337, 'data1 not stored'); assert(data2.a == 7331, 'data2 not stored'); } +#[test] +#[available_gas(60000000)] +fn bench_execute() { + let world = spawn_test_world(array![foo::TEST_CLASS_HASH],); + let bar_contract = IbarDispatcher { + contract_address: deploy_with_world_address(bar::TEST_CLASS_HASH, world) + }; + + let alice = starknet::contract_address_const::<0x1337>(); + starknet::testing::set_contract_address(alice); + + let gas = testing::get_available_gas(); + gas::withdraw_gas().unwrap(); + bar_contract.set_foo(1337, 1337); + end(gas, 'foo set call'); + + let gas = testing::get_available_gas(); + gas::withdraw_gas().unwrap(); + let data = get!(world, alice, Foo); + end(gas, 'foo get macro'); + + assert(data.a == 1337, 'data not stored'); +} + +#[test] +#[available_gas(60000000)] +fn bench_execute_complex() { + let world = spawn_test_world(array![foo::TEST_CLASS_HASH],); + let bar_contract = IbarDispatcher { + contract_address: deploy_with_world_address(bar::TEST_CLASS_HASH, world) + }; + + let alice = starknet::contract_address_const::<0x1337>(); + starknet::testing::set_contract_address(alice); + + let gas = testing::get_available_gas(); + gas::withdraw_gas().unwrap(); + bar_contract.set_char(1337, 1337); + end(gas, 'char set call'); + + let gas = testing::get_available_gas(); + gas::withdraw_gas().unwrap(); + let data = get!(world, alice, Character); + end(gas, 'char get macro'); + + assert(data.heigth == 1337, 'data not stored'); +} \ No newline at end of file diff --git a/crates/dojo-erc/Scarb.lock b/crates/dojo-erc/Scarb.lock new file mode 100644 index 0000000000..b00f21f531 --- /dev/null +++ b/crates/dojo-erc/Scarb.lock @@ -0,0 +1,20 @@ +# Code generated by scarb DO NOT EDIT. +version = 1 + +[[package]] +name = "dojo" +version = "0.3.1" +dependencies = [ + "dojo_plugin", +] + +[[package]] +name = "dojo_erc" +version = "0.3.1" +dependencies = [ + "dojo", +] + +[[package]] +name = "dojo_plugin" +version = "0.3.1" diff --git a/scripts/cairo_bench.sh b/scripts/cairo_bench.sh index a95e77a04d..877a73a52a 100755 --- a/scripts/cairo_bench.sh +++ b/scripts/cairo_bench.sh @@ -1,7 +1,7 @@ #!/bin/bash if [ "$#" -lt 1 ]; then - echo "Usage: $0 [set|diff]" + echo "Usage: $0 [set|diff|assert]" exit 1 fi @@ -20,4 +20,9 @@ if [ $1 = "set" ]; then run | sort | tee benches.txt elif [ $1 = "diff" ]; then run | sort | diff benches.txt - +elif [ $1 = "assert" ]; then + run | sort | cmp benches.txt - +else + echo "Usage: $0 [set|diff|assert]" + exit 1 fi