-
Notifications
You must be signed in to change notification settings - Fork 46
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
Refactor arrays to avoid clones according to the cost model. #1100
Conversation
Benchmark results Main vs HEAD.Base
Head
Base
Head
Base
Head
Base
Head
Base
Head
Base
Head
|
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #1100 +/- ##
==========================================
+ Coverage 81.03% 81.08% +0.04%
==========================================
Files 110 110
Lines 30204 29535 -669
==========================================
- Hits 24477 23948 -529
+ Misses 5727 5587 -140 ☔ View full report in Codecov by Sentry. |
Benchmarking resultsBenchmark for program
|
Command | Mean [s] | Min [s] | Max [s] | Relative |
---|---|---|---|---|
Cairo-vm (Rust, Cairo 1) |
20.585 ± 0.232 | 20.343 | 21.097 | 5.38 ± 0.09 |
cairo-native (embedded AOT) |
3.824 ± 0.044 | 3.789 | 3.945 | 1.00 |
cairo-native (embedded JIT using LLVM's ORC Engine) |
3.991 ± 0.080 | 3.833 | 4.119 | 1.04 ± 0.02 |
Benchmark for program dict_snapshot
Open benchmarks
Command | Mean [s] | Min [s] | Max [s] | Relative |
---|---|---|---|---|
Cairo-vm (Rust, Cairo 1) |
6.039 ± 0.053 | 5.937 | 6.107 | 1.61 ± 0.02 |
cairo-native (embedded AOT) |
3.744 ± 0.032 | 3.710 | 3.827 | 1.00 |
cairo-native (embedded JIT using LLVM's ORC Engine) |
3.840 ± 0.022 | 3.812 | 3.878 | 1.03 ± 0.01 |
Benchmark for program factorial_2M
Open benchmarks
Command | Mean [s] | Min [s] | Max [s] | Relative |
---|---|---|---|---|
Cairo-vm (Rust, Cairo 1) |
14.203 ± 0.108 | 14.077 | 14.453 | 3.40 ± 0.06 |
cairo-native (embedded AOT) |
4.177 ± 0.062 | 4.069 | 4.238 | 1.00 |
cairo-native (embedded JIT using LLVM's ORC Engine) |
4.222 ± 0.061 | 4.153 | 4.327 | 1.01 ± 0.02 |
Benchmark for program fib_2M
Open benchmarks
Command | Mean [s] | Min [s] | Max [s] | Relative |
---|---|---|---|---|
Cairo-vm (Rust, Cairo 1) |
14.108 ± 0.065 | 13.994 | 14.208 | 3.82 ± 0.02 |
cairo-native (embedded AOT) |
3.696 ± 0.012 | 3.679 | 3.717 | 1.00 |
cairo-native (embedded JIT using LLVM's ORC Engine) |
3.761 ± 0.031 | 3.703 | 3.804 | 1.02 ± 0.01 |
Benchmark for program linear_search
Open benchmarks
Command | Mean [s] | Min [s] | Max [s] | Relative |
---|---|---|---|---|
Cairo-vm (Rust, Cairo 1) |
6.058 ± 0.029 | 6.023 | 6.103 | 1.60 ± 0.01 |
cairo-native (embedded AOT) |
3.778 ± 0.022 | 3.752 | 3.823 | 1.00 |
cairo-native (embedded JIT using LLVM's ORC Engine) |
3.936 ± 0.030 | 3.894 | 3.988 | 1.04 ± 0.01 |
Benchmark for program logistic_map
Open benchmarks
Command | Mean [s] | Min [s] | Max [s] | Relative |
---|---|---|---|---|
Cairo-vm (Rust, Cairo 1) |
5.919 ± 0.079 | 5.844 | 6.061 | 1.51 ± 0.04 |
cairo-native (embedded AOT) |
3.915 ± 0.091 | 3.810 | 4.065 | 1.00 |
cairo-native (embedded JIT using LLVM's ORC Engine) |
4.128 ± 0.042 | 4.086 | 4.214 | 1.05 ± 0.03 |
src/libfuncs/array.rs
Outdated
let array_ptr = block.gep( | ||
context, | ||
location, | ||
array_ptr, | ||
&[GepIndex::Const(data_prefix_size as i32)], | ||
IntegerType::new(context, 8).into(), | ||
)?; |
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 a little confused here. What's the difference between this array_ptr
and the one above (line 642) whose GepIndex
has a negative value of data_prefix_size
?
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.
The input array_ptr (obtained from the realloc) is the allocation pointer. The offset pointer (result of the gep) is the pointer that will be stored in the array, which points to the first element (after the reference counter and max_len prefix).
The same process applies, but in reverse at line 642. Realloc needs the pointer returned by the original allocation, so we need to undo the offset (thus the negative) to obtain the original pointer and realloc.
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.
Ah I see, thanks!
pub fn calc_data_prefix_offset(layout: Layout) -> usize { | ||
get_integer_layout(32) | ||
.extend(get_integer_layout(32)) | ||
.unwrap() | ||
.0 | ||
.align_to(layout.align()) | ||
.unwrap() | ||
.pad_to_align() | ||
.size() | ||
} | ||
|
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.
Could you change unwrap
for expect
?
Also, could you document why we need two integers at the start of the array?
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.
Should never fail, so I don't know if using expect makes sense. Would a comment explaining that it's impossible to trigger them work?
The integers are:
- Reference counter: literally the number of references to the allocation.
- Max length: The number of elements present in the allocation (not necessarily the length array/span being accessed, but the whole allocation). It is used to know how many elements to drop when freeing the allocation.
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.
Would a comment explaining that it's impossible to trigger them work?
That should be fine, but isn't an unwrap + comment the same as an expect?
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.
The integers are:
- Reference counter: literally the number of references to the allocation.
- Max length: The number of elements present in the allocation (not necessarily the length array/span being accessed, but the whole allocation). It is used to know how many elements to drop when freeing the allocation.
Could you add that text to the code? Maybe to lines 17-20 in this file.
@@ -520,62 +527,48 @@ impl AotContractExecutor { | |||
let tag = *unsafe { enum_ptr.cast::<u8>().as_ref() } as usize; | |||
let tag = tag & 0x01; // Filter out bits that are not part of the enum's tag. | |||
|
|||
// layout of both enum variants, both are a array of felts |
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 no longer valid? If it is, could you add it back?
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.
Could you update this file's top-level documentation with the new layout?
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.
Also, I think we should explain in a bit more detail how the drop mechanism works (and why we need the max_len field)
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.
The PR looks good to me, although I think the documentation could be improved. I left some comments with ideas on how to improve it.
Also, the build_pop
function (and overall the whole file) is much easier to understand now, nice work.
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.
Could you add some high-level documentation on how arrays operate? Some thing that could be mentioned are:
- About arrays
- They only grow
- Operations:
- append (mutates the array)
- pop front (doesn't mutate the array)
- About spans:
- They have the same structure as arrays
- They share the same double pointer, to propagate reallocs
- Mutations on the original array are not propagated (as array only grows and cannot modify previous element)
- Operations:
- pop front
- pop back
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.
Also, I think we should explain in a bit more detail how the drop mechanism works (and why we need the max_len field)
|
||
let array_allocation_ptr = block.gep( | ||
// TODO: Drop elements before array_start and between array_end and max length. |
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 TODO relevant? Should it be addressed in a diferent pull request?
// Signature: | ||
// Params: RangeCheck, Snapshot<Array<felt252>>, u32, u32 | ||
// Branches: | ||
// 0: RangeCheck, Snapshot<Array<felt252>> | ||
// 1: RangeCheck |
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.
Could you restore this comment? It's useful for understanding the behaviour of the function.
Checklist