Skip to content
This repository was archived by the owner on Jul 22, 2024. It is now read-only.

Commit 696c817

Browse files
Add benchmarking (#254)
* add test files * fix path * fix fibonacci.cairo * change test to use first_contract.cairo * add run_bench script and increase amount of iteratiions * save work * try with fibonacci * update names * save work * replace bench * use bench/internals for flamegraph * use mimalloc, fix bench * bench intenrals * use directly state * add bin for test * save work * add test bins * add bench scripts * use makefile to run scripts for benching * update paths * remove unnecessary operations * increase amount of iterations * increase amount of iterations * add new bin * re-add file * remove pedersen * add bench for fibonacci * fix build and bench * remove executionInfo and apply_to_copy * Add doc, update makefile, add fib * rollback bench/internals.rs: * add -N flag to hyperfine * restore Makefile * address clippy errors * Update src/testing/starknet_state.rs * use new method * add EOF in .gitignore * remove pub from fields * replace vec reference with slice --------- Co-authored-by: juanbono <[email protected]>
1 parent 3f275ab commit 696c817

23 files changed

+758
-37
lines changed

.gitignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ starknet-venv/
4242
default.profraw
4343
**.DS_Store
4444
/scripts/reports
45-
45+
__pycache__/
46+
.pytest_cache/
4647
# codecov report
4748
lcov.info

Cargo.lock

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+5
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ name = "starknet-rs"
33
version = "0.1.0"
44
edition = "2021"
55

6+
[features]
7+
default = ["with_mimalloc"]
8+
with_mimalloc = ["mimalloc"]
9+
610
[dependencies]
711
cairo-rs = { git = "https://github.com/lambdaclass/cairo-rs", package = "cairo-vm", rev = "eebfd612c5f969c759cb982e8fb777b2a1a1dab0" }
812
felt = { git = "https://github.com/lambdaclass/cairo-rs", package = "cairo-felt", rev = "eebfd612c5f969c759cb982e8fb777b2a1a1dab0" }
@@ -22,6 +26,7 @@ thiserror = "1.0.32"
2226
clap = { version = "4.1.8", features = ["derive"] }
2327
actix-web = "4.3.1"
2428
awc = "3.1.1"
29+
mimalloc = { version = "0.1.29", default-features = false, optional = true }
2530
hex = "0.4.3"
2631
cargo-llvm-cov = "0.5.14"
2732

Makefile

+7
Original file line numberDiff line numberDiff line change
@@ -89,3 +89,10 @@ heaptrack:
8989

9090
flamegraph: compile-cairo compile-starknet
9191
CARGO_PROFILE_RELEASE_DEBUG=true cargo flamegraph --root --bench internals
92+
93+
benchmark: compile-cairo compile-starknet
94+
cargo build --release --all-targets
95+
./scripts/bench-invoke.sh
96+
./scripts/bench-deploy-invoke.sh
97+
./scripts/bench-fibonacci.sh
98+
./scripts/bench-deploy.sh

README.md

+26-13
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,24 @@ StarkNet library in Rust, featuring [⚡cairo-rs⚡](https://github.com/lambdacl
1919
</div>
2020

2121
## Table of Contents
22-
- [Disclaimer](#%EF%B8%8F-disclaimer)
23-
- [About](#-about)
24-
- [Getting Started](#-getting-started)
25-
* [Dependencies](#dependencies)
26-
* [Installation](#installation)
27-
- [Usage](#-usage)
28-
* [Running simple contracts](#running-simple-contracts)
29-
* [Testing](#testing)
30-
- [Contributing](#-contributing)
31-
- [Related Projects](#-related-projects)
32-
- [Documentation](#-documentation)
33-
* [StarkNet](#starknet)
34-
- [License](#%EF%B8%8F-license)
22+
- [Table of Contents](#table-of-contents)
23+
- [⚠️ Disclaimer](#️-disclaimer)
24+
- [📖 About](#-about)
25+
- [🌅 Getting Started](#-getting-started)
26+
- [Dependencies](#dependencies)
27+
- [Installation](#installation)
28+
- [How to manually install the script dependencies](#how-to-manually-install-the-script-dependencies)
29+
- [🚀 Usage](#-usage)
30+
- [Running simple contracts](#running-simple-contracts)
31+
- [Using the Cli](#using-the-cli)
32+
- [Testing](#testing)
33+
- [Profiling](#profiling)
34+
- [Benchmarking](#benchmarking)
35+
- [🛠 Contributing](#-contributing)
36+
- [🌞 Related Projects](#-related-projects)
37+
- [📚 Documentation](#-documentation)
38+
- [StarkNet](#starknet)
39+
- [⚖️ License](#️-license)
3540

3641
## ⚠️ Disclaimer
3742

@@ -104,6 +109,14 @@ $ make flamegraph
104109

105110
to generate a flamegraph with info of the execution of the main operations.
106111

112+
### Benchmarking
113+
114+
Read the 'bench_integration.py' file to identify which lines need to be commented out for accurate results. Comment out those lines and then run the following command:
115+
116+
```bash
117+
$ make benchmark
118+
```
119+
107120
## 🛠 Contributing
108121

109122
The open source community is a fantastic place for learning, inspiration, and creation, and this is all thanks to contributions from people like you. Your contributions are **greatly appreciated**.

bench/internals.rs

-3
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ lazy_static! {
4545
fn scope<T>(f: impl FnOnce() -> T) -> T {
4646
f()
4747
}
48-
4948
// We don't use the cargo test harness because it uses
5049
// FnOnce calls for each test, that are merged in the flamegraph.
5150
fn main() {
@@ -170,9 +169,7 @@ fn invoke() {
170169
state
171170
.set_contract_class(&CLASS_HASH, &CONTRACT_CLASS)
172171
.unwrap();
173-
174172
let config = &Default::default();
175-
176173
let salt = Address(felt_str!(
177174
"2669425616857739096022668060305620640217901643963991674344872184515580705509"
178175
));

bench_integration.py

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import os
2+
3+
import pytest
4+
import pytest_asyncio
5+
from starkware.starknet.compiler.compile import compile_starknet_files
6+
from starkware.starknet.testing.starknet import StarknetState
7+
from starkware.starknet.services.api.contract_class import ContractClass
8+
CONTRACT_FILE = os.path.join(os.path.dirname(__file__), "starknet_programs/first_contract.json")
9+
FIBONACCI_FILE = os.path.join(os.path.dirname(__file__), "starknet_programs/fibonacci.json")
10+
11+
12+
@pytest.mark.asyncio
13+
async def test_invoke():
14+
runs = 10000
15+
starknet = await StarknetState.empty()
16+
json_program = open(CONTRACT_FILE).read()
17+
contract_class = ContractClass.loads(json_program)
18+
contract_address, _ = await starknet.deploy(contract_class=contract_class, constructor_calldata=[])
19+
for i in range(runs):
20+
# take into account that you need to comment verify_version()
21+
# from cairo-lang in order to be able to run the selectors below
22+
# because invoke_raw inserts a default version=0 that throws an
23+
# error.
24+
res_1 = await starknet.invoke_raw(contract_address=contract_address, selector="increase_balance", calldata=[1000], max_fee=0)
25+
res_2 = await starknet.invoke_raw(contract_address=contract_address, selector="get_balance", calldata=[], max_fee=0)
26+
assert(res_2.call_info.retdata == [i*1000 + 1000])
27+
28+
@pytest.mark.asyncio
29+
async def test_deploy():
30+
runs = 100
31+
starknet = await StarknetState.empty()
32+
json_program = open(CONTRACT_FILE).read()
33+
contract_class = ContractClass.loads(json_program)
34+
for i in range(runs):
35+
contract_address, _ = await starknet.deploy(contract_class=contract_class, constructor_calldata=[], contract_address_salt=i)
36+
37+
@pytest.mark.asyncio
38+
async def test_fibonacci():
39+
runs = 1000
40+
starknet = await StarknetState.empty()
41+
json_program = open(FIBONACCI_FILE).read()
42+
contract_class = ContractClass.loads(json_program)
43+
contract_address, _ = await starknet.deploy(contract_class=contract_class, constructor_calldata=[])
44+
for i in range(runs):
45+
call = await starknet.invoke_raw(contract_address=contract_address, selector="fib", calldata=[1, 1, 1000], max_fee=0)
46+
assert(call.call_info.retdata == [222450955505511890955301767713383614666194461405743219770606958667979327682])

scripts/bench-deploy-invoke.sh

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#!/usr/bin/env sh
2+
hyperfine -N -w 3 -r 5 \
3+
-n "cairo-lang (CPython) read/write storage 10k with deploy" "pytest bench_integration.py::test_invoke" \
4+
-n "starknet_in_rust read/write storage 10k with deploy" "./target/release/deploy_invoke"

scripts/bench-deploy.sh

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#!/usr/bin/env sh
2+
hyperfine -N -w 3 -r 5 \
3+
-n "cairo-lang (CPython) deploy 10k" "pytest bench_integration.py::test_deploy" \
4+
-n "starknet_in_rust deploy 10k" "./target/release/deploy"

scripts/bench-fibonacci.sh

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#!/usr/bin/env sh
2+
hyperfine -N -w 3 -r 5 \
3+
-n "cairo-lang (CPython) fib 15k" "pytest bench_integration.py::test_fibonacci" \
4+
-n "starknet_in_rust fib 15k" "./target/release/fibonacci"

scripts/bench-invoke.sh

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#!/usr/bin/env sh
2+
hyperfine -N -w 3 -r 5 \
3+
-n "cairo-lang (CPython) read/write storage 10k" "pytest bench_integration.py::test_invoke" \
4+
-n "starknet_in_rust read/write storage 10k without deploy" "./target/release/invoke"

src/bin/deploy.rs

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
use std::path::PathBuf;
2+
3+
use lazy_static::lazy_static;
4+
use starknet_rs::{
5+
services::api::contract_class::ContractClass, testing::starknet_state::StarknetState,
6+
utils::Address,
7+
};
8+
9+
#[cfg(feature = "with_mimalloc")]
10+
use mimalloc::MiMalloc;
11+
12+
#[cfg(feature = "with_mimalloc")]
13+
#[global_allocator]
14+
static ALLOC: MiMalloc = MiMalloc;
15+
16+
lazy_static! {
17+
// include_str! doesn't seem to work in CI
18+
static ref CONTRACT_CLASS: ContractClass = ContractClass::try_from(PathBuf::from(
19+
"starknet_programs/first_contract.json",
20+
)).unwrap();
21+
}
22+
23+
fn main() {
24+
const RUNS: usize = 100;
25+
let mut starknet_state = StarknetState::new(None);
26+
27+
for n in 0..RUNS {
28+
let contract_address_salt = Address(n.into());
29+
30+
starknet_state
31+
.deploy(CONTRACT_CLASS.clone(), vec![], contract_address_salt)
32+
.unwrap();
33+
}
34+
}

src/bin/deploy_invoke.rs

+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
use std::path::PathBuf;
2+
3+
use felt::{felt_str, Felt252};
4+
use num_traits::Zero;
5+
6+
use starknet_rs::{
7+
services::api::contract_class::ContractClass, testing::starknet_state::StarknetState,
8+
utils::Address,
9+
};
10+
11+
use lazy_static::lazy_static;
12+
13+
#[cfg(feature = "with_mimalloc")]
14+
use mimalloc::MiMalloc;
15+
16+
#[cfg(feature = "with_mimalloc")]
17+
#[global_allocator]
18+
static ALLOC: MiMalloc = MiMalloc;
19+
20+
lazy_static! {
21+
// include_str! doesn't seem to work in CI
22+
static ref CONTRACT_CLASS: ContractClass = ContractClass::try_from(PathBuf::from(
23+
"starknet_programs/first_contract.json",
24+
)).unwrap();
25+
26+
static ref CONTRACT_PATH: PathBuf = PathBuf::from("starknet_programs/first_contract.json");
27+
28+
static ref CONTRACT_CLASS_HASH: [u8; 32] = [5, 133, 114, 83, 104, 231, 159, 23, 87, 255, 235, 75, 170, 4, 84, 140, 49, 77, 101, 41, 147, 198, 201, 231, 38, 189, 215, 84, 231, 141, 140, 122];
29+
30+
static ref CONTRACT_ADDRESS: Address = Address(1.into());
31+
32+
static ref INCREASE_BALANCE_SELECTOR: Felt252 = felt_str!("1530486729947006463063166157847785599120665941190480211966374137237989315360");
33+
34+
static ref GET_BALANCE_SELECTOR: Felt252 = felt_str!("1636223440827086009537493065587328807418413867743950350615962740049133672085");
35+
}
36+
37+
fn main() {
38+
const RUNS: usize = 10000;
39+
let mut starknet_state = StarknetState::new(None);
40+
let contract_address_salt = Address(1.into());
41+
42+
let (contract_address, _exec_info) = starknet_state
43+
.deploy(CONTRACT_CLASS.to_owned(), vec![], contract_address_salt)
44+
.unwrap();
45+
46+
// Statement **not** in blockifier.
47+
starknet_state
48+
.state
49+
.cache_mut()
50+
.nonce_initial_values_mut()
51+
.insert(contract_address.clone(), Felt252::zero());
52+
53+
for i in 0..RUNS {
54+
starknet_state
55+
.invoke_raw(
56+
contract_address.clone(),
57+
INCREASE_BALANCE_SELECTOR.clone(),
58+
vec![1000.into()],
59+
0,
60+
Some(Vec::new()),
61+
Some(Felt252::from(i * 2)),
62+
)
63+
.unwrap();
64+
65+
let tx_exec_info = starknet_state
66+
.invoke_raw(
67+
contract_address.clone(),
68+
GET_BALANCE_SELECTOR.clone(),
69+
vec![],
70+
0,
71+
Some(Vec::new()),
72+
Some(Felt252::from((i * 2) + 1)),
73+
)
74+
.unwrap();
75+
76+
assert_eq!(
77+
tx_exec_info.call_info.unwrap().retdata,
78+
vec![((1000 * i) + 1000).into()]
79+
);
80+
}
81+
}

src/bin/fibonacci.rs

+95
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
use std::{collections::HashMap, path::PathBuf};
2+
3+
use felt::{felt_str, Felt252};
4+
use num_traits::Zero;
5+
6+
use lazy_static::lazy_static;
7+
use starknet_rs::{
8+
business_logic::{
9+
fact_state::in_memory_state_reader::InMemoryStateReader, state::cached_state::CachedState,
10+
},
11+
services::api::contract_class::ContractClass,
12+
testing::starknet_state::StarknetState,
13+
utils::Address,
14+
};
15+
16+
#[cfg(feature = "with_mimalloc")]
17+
use mimalloc::MiMalloc;
18+
19+
#[cfg(feature = "with_mimalloc")]
20+
#[global_allocator]
21+
static ALLOC: MiMalloc = MiMalloc;
22+
23+
lazy_static! {
24+
// include_str! doesn't seem to work in CI
25+
static ref CONTRACT_CLASS: ContractClass = ContractClass::try_from(PathBuf::from(
26+
"starknet_programs/fibonacci.json",
27+
)).unwrap();
28+
29+
static ref CONTRACT_PATH: PathBuf = PathBuf::from("starknet_programs/fibonacci.json");
30+
31+
static ref CONTRACT_CLASS_HASH: [u8; 32] = [1; 32];
32+
33+
static ref CONTRACT_ADDRESS: Address = Address(1.into());
34+
35+
static ref FIB_SELECTOR: Felt252 = felt_str!("485685360977693822178494178685050472186234432883326654755380582597179924681");
36+
37+
static ref EXPECTED_RES: Felt252 = felt_str!("222450955505511890955301767713383614666194461405743219770606958667979327682");
38+
}
39+
40+
fn main() {
41+
const RUNS: usize = 1000;
42+
let cached_state = create_initial_state();
43+
44+
let mut starknet_state = StarknetState::new_with_states(Default::default(), cached_state);
45+
46+
starknet_state
47+
.state
48+
.cache_mut()
49+
.nonce_initial_values_mut()
50+
.insert(CONTRACT_ADDRESS.clone(), Felt252::zero());
51+
52+
for i in 0..RUNS {
53+
let tx_exec_info = starknet_state
54+
.invoke_raw(
55+
CONTRACT_ADDRESS.clone(),
56+
FIB_SELECTOR.clone(),
57+
[1.into(), 1.into(), 1000.into()].into(),
58+
0,
59+
Some(Vec::new()),
60+
Some(Felt252::from(i)),
61+
)
62+
.unwrap();
63+
64+
assert_eq!(
65+
tx_exec_info.call_info.unwrap().retdata,
66+
vec![EXPECTED_RES.clone()]
67+
)
68+
}
69+
}
70+
71+
fn create_initial_state() -> CachedState<InMemoryStateReader> {
72+
let cached_state = CachedState::new(
73+
{
74+
let mut state_reader = InMemoryStateReader::default();
75+
state_reader
76+
.address_to_class_hash_mut()
77+
.insert(CONTRACT_ADDRESS.clone(), *CONTRACT_CLASS_HASH);
78+
79+
state_reader
80+
.address_to_nonce_mut()
81+
.insert(CONTRACT_ADDRESS.clone(), Felt252::zero());
82+
state_reader
83+
.class_hash_to_contract_class_mut()
84+
.insert(*CONTRACT_CLASS_HASH, CONTRACT_CLASS.clone());
85+
86+
state_reader
87+
.address_to_storage_mut()
88+
.insert((CONTRACT_ADDRESS.clone(), [0; 32]), Felt252::zero());
89+
state_reader
90+
},
91+
Some(HashMap::new()),
92+
);
93+
94+
cached_state
95+
}

0 commit comments

Comments
 (0)