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

Add flamegraph target to Makefile #205

Merged
merged 18 commits into from
Mar 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ num-traits = "0.2.15"
serde = { version = "1.0.152", features = ["derive"] }
serde_json = { version = "1.0", features = ["arbitrary_precision"] }
sha3 = "0.10.1"
starknet_api = { git = "https://github.com/lambdaclass/starknet-api", branch = "main", features = ["testing"] }
starknet_api = { git = "https://github.com/lambdaclass/starknet-api", branch = "main", features = [
"testing",
] }
starknet-crypto = "0.2.0"
thiserror = { version = "1.0.32" }
clap = { version = "4.1.8", features = ["derive"] }
Expand All @@ -26,6 +28,9 @@ assert_matches = "1.5.0"
rusty-hook = "0.11"

[workspace]
members = [
"crates/starknet-rs-py",
]
members = ["crates/starknet-rs-py"]

[[bench]]
path = "bench/internals.rs"
name = "internals"
harness = false
16 changes: 9 additions & 7 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ compile-cairo: $(CAIRO_TARGETS)
compile-starknet: $(STARKNET_TARGETS)

cairo_programs/%.json: cairo_programs/%.cairo
cd cairo_programs/ && cairo-compile $(shell grep "^// @compile-flags += .*$$" $< | cut -c 22-) ../$< --output ../$@ || rm ../$@
. starknet-venv/bin/activate && cd cairo_programs/ && cairo-compile $(shell grep "^// @compile-flags += .*$$" $< | cut -c 22-) ../$< --output ../$@ || rm ../$@

starknet_programs/%.json: starknet_programs/%.cairo
cd starknet_programs/ && starknet-compile $(shell grep "^// @compile-flags += .*$$" $< | cut -c 22-) ../$< --output ../$@ || rm ../$@
. starknet-venv/bin/activate && cd starknet_programs/ && starknet-compile $(shell grep "^// @compile-flags += .*$$" $< | cut -c 22-) ../$< --output ../$@ || rm ../$@


#
Expand All @@ -47,6 +47,7 @@ check:

deps:
cargo install cargo-tarpaulin --version 0.23.1
cargo install flamegraph --version 0.6.2
python3 -m venv starknet-venv
. starknet-venv/bin/activate && $(MAKE) deps-venv

Expand All @@ -60,15 +61,16 @@ clean:
clippy:
cargo clippy --all-targets -- -D warnings

test:
. starknet-venv/bin/activate && $(MAKE) compile-cairo compile-starknet
test: compile-cairo compile-starknet
cargo test

py-test:
py-test: compile-cairo compile-starknet
. starknet-venv/bin/activate
cargo test -p starknet-rs-py --no-default-features --features embedded-python

coverage:
. starknet-venv/bin/activate && $(MAKE) compile-cairo compile-starknet
coverage: compile-cairo compile-starknet
cargo tarpaulin
-rm -f default.profraw

flamegraph: compile-cairo compile-starknet
CARGO_PROFILE_RELEASE_DEBUG=true cargo flamegraph --root --bench internals
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,22 @@ You can find a tutorial on running contracts [here](/contract_execution_examples
You can find an example on how to use the cli [here](/docs/CLI_USAGE_EXAMPLE.md)

### Testing

Run the following command:
```bash
$ make test
```

### Profiling

Run the following command:

```bash
$ make flamegraph
```

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

## 🛠 Contributing

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**.
Expand Down
206 changes: 206 additions & 0 deletions bench/internals.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
#![deny(warnings)]

use felt::{felt_str, Felt};
use lazy_static::lazy_static;
use num_traits::Zero;
use starknet_rs::{
business_logic::{
fact_state::in_memory_state_reader::InMemoryStateReader,
state::{cached_state::CachedState, state_api::State},
transaction::objects::{
internal_declare::InternalDeclare, internal_deploy::InternalDeploy,
internal_deploy_account::InternalDeployAccount,
internal_invoke_function::InternalInvokeFunction,
},
},
core::contract_address::starknet_contract_address::compute_class_hash,
definitions::general_config::StarknetChainId,
public::abi::VALIDATE_ENTRY_POINT_SELECTOR,
services::api::contract_class::ContractClass,
utils::{felt_to_hash, Address},
};
use std::{hint::black_box, path::PathBuf};

lazy_static! {
// include_str! doesn't seem to work in CI
static ref CONTRACT_CLASS: ContractClass = ContractClass::try_from(PathBuf::from(
"starknet_programs/account_without_validation.json",
))
.unwrap();
Comment on lines +26 to +29
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any way to build the ContractClass from bytes? In that case you could use include_bytes! instead of creating a path that needs to be read from.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The GH workflow that runs clippy doesn't install deps and so cannot compile the cairo programs. I'll revert this change.

static ref CLASS_HASH: [u8; 32] = felt_to_hash(&compute_class_hash(
&CONTRACT_CLASS
).unwrap());
static ref CONTRACT_ADDRESS: Address = Address(felt_str!(
"3577223136242220508961486249701638158054969090851914040041358274796489907314"
));
static ref SIGNATURE: Vec<Felt> = vec![
felt_str!("3233776396904427614006684968846859029149676045084089832563834729503047027074"),
felt_str!("707039245213420890976709143988743108543645298941971188668773816813012281203"),
];
}

// This function just executes the given function. This adds a stack level
// to the flamegraph with the label "scope".
#[inline(never)]
fn scope<T>(f: impl FnOnce() -> T) -> T {
f()
}

// We don't use the cargo test harness because it uses
// FnOnce calls for each test, that are merged in the flamegraph.
fn main() {
deploy_account();
declare();
deploy();
invoke();

// The black_box ensures there's no tail-call optimization.
// If not, the flamegraph ends up less nice.
black_box(());
}

#[inline(never)]
fn deploy_account() {
const RUNS: usize = 500;

let state_reader = InMemoryStateReader::default();
let mut state = CachedState::new(state_reader, Some(Default::default()));

state
.set_contract_class(&CLASS_HASH, &CONTRACT_CLASS)
.unwrap();

let config = &Default::default();

for _ in 0..RUNS {
let mut state_copy = state.clone();
let salt = Address(felt_str!(
"2669425616857739096022668060305620640217901643963991674344872184515580705509"
));
let class_hash = *CLASS_HASH;
let signature = SIGNATURE.clone();
scope(|| {
// new consumes more execution time than raw struct instantiation
let internal_deploy_account = InternalDeployAccount::new(
class_hash,
0,
0,
Felt::zero(),
vec![],
signature,
salt,
StarknetChainId::TestNet,
)
.unwrap();
internal_deploy_account.execute(&mut state_copy, config)
})
.unwrap();
}
}

#[inline(never)]
fn declare() {
const RUNS: usize = 5;

let state_reader = InMemoryStateReader::default();
let state = CachedState::new(state_reader, Some(Default::default()));

let config = &Default::default();

for _ in 0..RUNS {
let mut cloned_state = state.clone();
let class = CONTRACT_CLASS.clone();
let address = CONTRACT_ADDRESS.clone();
scope(|| {
// new consumes more execution time than raw struct instantiation
let declare_tx = InternalDeclare::new(
class,
StarknetChainId::TestNet.to_felt(),
address,
0,
0,
vec![],
Felt::zero(),
)
.expect("couldn't create transaction");

declare_tx.execute(&mut cloned_state, config)
})
.unwrap();
}
}

#[inline(never)]
fn deploy() {
const RUNS: usize = 8;

let state_reader = InMemoryStateReader::default();
let mut state = CachedState::new(state_reader, Some(Default::default()));

state
.set_contract_class(&CLASS_HASH, &CONTRACT_CLASS)
.unwrap();

let config = &Default::default();

for _ in 0..RUNS {
let mut state_copy = state.clone();
let salt = Address(felt_str!(
"2669425616857739096022668060305620640217901643963991674344872184515580705509"
));
let class = CONTRACT_CLASS.clone();
scope(|| {
// new consumes more execution time than raw struct instantiation
let internal_deploy =
InternalDeploy::new(salt, class, vec![], StarknetChainId::TestNet.to_felt(), 0)
.unwrap();
internal_deploy.execute(&mut state_copy, config)
})
.unwrap();
}
}

#[inline(never)]
fn invoke() {
const RUNS: usize = 100;

let state_reader = InMemoryStateReader::default();
let mut state = CachedState::new(state_reader, Some(Default::default()));

state
.set_contract_class(&CLASS_HASH, &CONTRACT_CLASS)
.unwrap();

let config = &Default::default();

let salt = Address(felt_str!(
"2669425616857739096022668060305620640217901643963991674344872184515580705509"
));
let class = CONTRACT_CLASS.clone();
let internal_deploy =
InternalDeploy::new(salt, class, vec![], StarknetChainId::TestNet.to_felt(), 0).unwrap();
internal_deploy.execute(&mut state, config).unwrap();

for _ in 0..RUNS {
let mut state_copy = state.clone();
let address = CONTRACT_ADDRESS.clone();
let selector = VALIDATE_ENTRY_POINT_SELECTOR.clone();
let signature = SIGNATURE.clone();
let calldata = vec![address.0.clone(), selector.clone(), Felt::zero()];
scope(|| {
// new consumes more execution time than raw struct instantiation
let internal_invoke = InternalInvokeFunction::new(
address,
selector,
0,
calldata,
signature,
StarknetChainId::TestNet.to_felt(),
Some(Felt::zero()),
)
.unwrap();
internal_invoke.execute(&mut state_copy, config)
})
.unwrap();
}
}
2 changes: 1 addition & 1 deletion src/business_logic/transaction/objects/internal_deploy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ impl InternalDeploy {
&self,
state: &mut T,
) -> Result<TransactionExecutionInfo, StarkwareError> {
if self.constructor_calldata.is_empty() {
if !self.constructor_calldata.is_empty() {
return Err(StarkwareError::TransactionFailed);
}

Expand Down
9 changes: 9 additions & 0 deletions src/services/api/contract_class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,15 @@ impl From<starknet_api::state::ContractClass> for ContractClass {
// Helper Functions
// -------------------

impl TryFrom<&str> for ContractClass {
type Error = io::Error;

fn try_from(s: &str) -> io::Result<Self> {
let raw_contract_class: starknet_api::state::ContractClass = serde_json::from_str(s)?;
Ok(ContractClass::from(raw_contract_class))
}
}

impl TryFrom<PathBuf> for ContractClass {
type Error = io::Error;

Expand Down
23 changes: 7 additions & 16 deletions src/testing/starknet_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,6 @@ mod tests {
let mut starknet_state = StarknetState::new(None);
let path = PathBuf::from("starknet_programs/fibonacci.json");
let contract_class = ContractClass::try_from(path).unwrap();
let constructor_calldata = [1.into(), 1.into(), 10.into()].to_vec();
let contract_address_salt = Address(1.into());

// expected results
Expand All @@ -311,7 +310,7 @@ mod tests {
let class_hash = felt_to_hash(&hash);

let address = Address(felt_str!(
"3173424428166065804253636112972198402746524727884605069568266184332607747575"
"2066790681318687707025847340457605657642478884993868155391041767964612021885"
));

let mut actual_resources = HashMap::new();
Expand Down Expand Up @@ -339,11 +338,7 @@ mod tests {
let exec = (address, transaction_exec_info);
assert_eq!(
starknet_state
.deploy(
contract_class.clone(),
constructor_calldata,
contract_address_salt
)
.deploy(contract_class.clone(), vec![], contract_address_salt)
.unwrap(),
exec
);
Expand Down Expand Up @@ -476,15 +471,11 @@ mod tests {
let mut starknet_state = StarknetState::new(None);
let path = PathBuf::from("starknet_programs/fibonacci.json");
let contract_class = ContractClass::try_from(path).unwrap();
let constructor_calldata = [1.into(), 1.into(), 10.into()].to_vec();
let calldata = [1.into(), 1.into(), 10.into()].to_vec();
let contract_address_salt = Address(1.into());

let (contract_address, _exec_info) = starknet_state
.deploy(
contract_class.clone(),
constructor_calldata.clone(),
contract_address_salt,
)
.deploy(contract_class.clone(), vec![], contract_address_salt)
.unwrap();

// fibonacci selector
Expand All @@ -505,7 +496,7 @@ mod tests {
.invoke_raw(
contract_address,
selector.clone(),
constructor_calldata,
calldata,
0,
Some(Vec::new()),
Some(Felt::zero()),
Expand All @@ -518,7 +509,7 @@ mod tests {
let fib_class_hash = felt_to_hash(&hash);

let address = felt_str!(
"3173424428166065804253636112972198402746524727884605069568266184332607747575"
"2066790681318687707025847340457605657642478884993868155391041767964612021885"
);
let mut actual_resources = HashMap::new();
actual_resources.insert("l1_gas_usage".to_string(), 0);
Expand All @@ -542,6 +533,6 @@ mod tests {
..Default::default()
};

assert_eq!(expected_info, tx_info);
assert_eq!(tx_info, expected_info);
}
}
Loading