-
Notifications
You must be signed in to change notification settings - Fork 47
Implement Libfunc Counter #1267
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
✅ Code is now correctly formatted. |
d6d1365
to
09ec4d1
Compare
Benchmark results Main vs HEAD.Base
Head
Base
Head
Base
Head
Base
Head
Base
Head
Base
Head
|
Benchmarking resultsBenchmark for program
|
Command | Mean [s] | Min [s] | Max [s] | Relative |
---|---|---|---|---|
Cairo-vm (Rust, Cairo 1) |
11.329 ± 0.037 | 11.281 | 11.411 | 2.29 ± 0.02 |
cairo-native (embedded AOT) |
4.937 ± 0.030 | 4.888 | 4.974 | 1.00 |
cairo-native (embedded JIT using LLVM's ORC Engine) |
5.030 ± 0.049 | 4.974 | 5.146 | 1.02 ± 0.01 |
Benchmark for program dict_snapshot
Open benchmarks
Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
---|---|---|---|---|
Cairo-vm (Rust, Cairo 1) |
822.6 ± 7.7 | 813.7 | 840.3 | 1.00 |
cairo-native (embedded AOT) |
4975.2 ± 33.1 | 4944.0 | 5036.0 | 6.05 ± 0.07 |
cairo-native (embedded JIT using LLVM's ORC Engine) |
5079.8 ± 38.0 | 4985.5 | 5137.2 | 6.18 ± 0.07 |
Benchmark for program factorial_2M
Open benchmarks
Command | Mean [s] | Min [s] | Max [s] | Relative |
---|---|---|---|---|
Cairo-vm (Rust, Cairo 1) |
5.310 ± 0.061 | 5.259 | 5.425 | 1.00 |
cairo-native (embedded AOT) |
5.377 ± 0.033 | 5.320 | 5.437 | 1.01 ± 0.01 |
cairo-native (embedded JIT using LLVM's ORC Engine) |
5.434 ± 0.029 | 5.398 | 5.480 | 1.02 ± 0.01 |
Benchmark for program fib_2M
Open benchmarks
Command | Mean [s] | Min [s] | Max [s] | Relative |
---|---|---|---|---|
Cairo-vm (Rust, Cairo 1) |
5.165 ± 0.090 | 5.093 | 5.399 | 1.05 ± 0.02 |
cairo-native (embedded AOT) |
4.923 ± 0.040 | 4.878 | 4.991 | 1.00 |
cairo-native (embedded JIT using LLVM's ORC Engine) |
4.952 ± 0.028 | 4.902 | 5.007 | 1.01 ± 0.01 |
Benchmark for program linear_search
Open benchmarks
Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
---|---|---|---|---|
Cairo-vm (Rust, Cairo 1) |
852.3 ± 9.5 | 838.4 | 865.5 | 1.00 |
cairo-native (embedded AOT) |
5057.0 ± 39.4 | 4999.9 | 5132.3 | 5.93 ± 0.08 |
cairo-native (embedded JIT using LLVM's ORC Engine) |
5179.2 ± 52.4 | 5115.6 | 5280.7 | 6.08 ± 0.09 |
Benchmark for program logistic_map
Open benchmarks
Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
---|---|---|---|---|
Cairo-vm (Rust, Cairo 1) |
550.6 ± 4.8 | 544.8 | 560.8 | 1.00 |
cairo-native (embedded AOT) |
5076.0 ± 31.1 | 5028.7 | 5119.5 | 9.22 ± 0.10 |
cairo-native (embedded JIT using LLVM's ORC Engine) |
5242.0 ± 52.6 | 5138.1 | 5307.6 | 9.52 ± 0.13 |
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #1267 +/- ##
==========================================
- Coverage 80.12% 80.01% -0.11%
==========================================
Files 112 112
Lines 30068 30145 +77
==========================================
+ Hits 24093 24122 +29
- Misses 5975 6023 +48 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
0b0d8a6
to
81511e1
Compare
3e1a484
to
06aef99
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some thoughts related to this file:
- Why is the libfunc counter API separated in two modules instead of using only one? For example, the compiler calls
count_libfunc
from the runtime module butbuild_array_counter
andstore_array_counter
are called from the root module. - If it is necessary to have two separated modules for the API then it would be great if the documentation includes what does each module do and (if it is possible or makes sense) the reason behind this separation in modules.
- In line 4 I would not call the three methods listed below as
important
. If they are the only methods used by external files to count libfuncs then these 3 methods are the Libfunc Counter API.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- To have a separation from what belongs to MLIR, and what belongs to Rust (runtime).
- Yes, I'll add docs if you agree with (1.).
- I can change it to
main
if you prefer.
f8f1460
to
71ad878
Compare
af29bd2
to
d5a557d
Compare
a7fb2cf
to
470bc78
Compare
470bc78
to
3ef6949
Compare
@@ -57,6 +59,11 @@ struct Args { | |||
/// The output path for the libfunc profilling results | |||
profiler_output: Option<PathBuf>, | |||
|
|||
#[cfg(feature = "with-libfunc-counter")] | |||
#[arg(long)] | |||
/// The output path for the execution trace |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this comment valid?
#[cfg(feature = "with-libfunc-counter")] | ||
let libfunc_indexes = program | ||
.libfunc_declarations | ||
.iter() | ||
.enumerate() | ||
.map(|(idx, libf)| (libf.id.clone(), idx)) | ||
.collect::<HashMap<ConcreteLibfuncId, usize>>(); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this mapping necessary? The libfunc ID already seems to be autoincremental, so maybe we can use the libfunc ID directly.
pub fn count_libfunc( | ||
&mut self, | ||
context: &Context, | ||
module: &Module, | ||
block: &Block<'_>, | ||
location: Location, | ||
libfunc_idx: usize, | ||
) -> Result<()> { | ||
let u32_ty = IntegerType::new(context, 32).into(); | ||
let k1 = block.const_int(context, location, 1, 32)?; | ||
|
||
let array_counter_ptr = self.get_array_counter(context, module, block, location)?; | ||
|
||
let value_counter_ptr = block.gep( | ||
context, | ||
location, | ||
array_counter_ptr, | ||
&[GepIndex::Const(libfunc_idx as i32)], | ||
u32_ty, | ||
)?; | ||
|
||
let value_counter = block.load(context, location, value_counter_ptr, u32_ty)?; | ||
let value_incremented = block.addi(value_counter, k1, location)?; | ||
|
||
block.store(context, location, value_counter_ptr, value_incremented)?; | ||
|
||
Ok(()) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not convinced that this is the right approach.
Right know, to obtain the pointer to the array, we must call an extern function get_array_counter
. This is more complex than required. I think this feature can be achieved without relying on invoking Rust functions from the compiled code, and without relying on a global hashmap.
- Have each contract declare an MLIR global variable called
libfunc_counts
(or so). It should contain a pointer to a dinamically allocated array. - Before executing each statement, obtain the array pointer, find the index of the associated libfunc, and increment it by 1. We should be able to obtain the array pointer by accessing the MLIR global previously defined.
- At the start of the execution, inside of
invoke_dynamic
, orrun
, alloc the array and set global's value to the arrays pointer. At the end of the execution, don't forget to replace the global's value with the previous pointer. - To expose the libfunc counts to the user, we could return it from the
invoke_dynamic
/run
functions.
This way, the functionality would be completely hidden from the user (as opposed to the current approach, that requires changes in cairo-native-run
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree It would be better for the array to be built and restored inside either invoke_dynamic
or run
. It should be possible with the current implementation. However I don't so much complexity in calling the a rust function. Despite this, it could be done if what we want is to avoid rust as much as possible. I actually did it this way because I thought it was simpler.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are right, there are two different orthogonal improvements that can be made:
- Have
invoke_dynamic/run
handle the logic for setting up and reading the libfunc counters. - Use a MLIR global rather than calling a Rust function.
We could do the first improvements without changing how we obtain the array pointer. However, using an MLIR global should be more performant and require less code than calling a Rust function.
Implements the Libfunc Counter. Used to count every call of each libfunc in an execution. All libfuncs that have been compiled will be taken into account (even if they never get called, in which case the counter will be 0). An example of execution a program with this feature is as follows:
Checklist