-
Notifications
You must be signed in to change notification settings - Fork 3.7k
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
[framework] smart_vector and big_vector #5690
Conversation
without actual reviewing: should delete |
b21661a
to
22aa836
Compare
(sorry, accidental unqualified approval) |
/// `SmartVector` should not enforece that. | ||
struct SmartVector<T> has store { | ||
inline_inner: vector<T>, | ||
table_inner: vector<BigVector<T>>, |
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.
why is this vector<BigVector> not BigVector?
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.
vector
here is actually an option
w/o Drop
ability requirement of T
to save space of a table handle when the smart_vector is small.
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.
can we have some documentation here? otherwise, it is quite hard to understand why having a vector here.
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.
+1 on adding some quick comments to explain
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.
This (or something similar) may help to clarify:
spec struct SmartVector {
invariant vector::length(inline_inner) + vector::length(table_inner) == 1;
}
22aa836
to
3093db5
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.
Just some relatively minor comments. This makes sense in general but I'll take a much closer look after design discussion.
assert!(i < len, error::invalid_argument(EINDEX_OUT_OF_BOUNDS)); | ||
let val = swap_remove(v, i); | ||
if (i + 1 < len - 1) { | ||
range_reverse(v, i + 1, len - 1); |
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.
Why are we using range_reserve instead of just simply shifting every element to the left by one (e.g. swapping)?
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.
That seems better. Yeah, let me change it.
/// Return if an element equal to e exists in the vector v. | ||
/// disclaimer: this function is costly. Use it at your own discretion. | ||
public fun contains<T>(v: &BigVector<T>, val: &T): bool { | ||
if (is_empty(v)) return false; |
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.
IMO this doesn't save much gas vs calling index_of as the main loop there would not run anyway. It can therefore be removed for simplicity.
length(v) == 0 | ||
} | ||
|
||
#[test_only] |
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.
Can we add more tests with more edge cases?
/// `SmartVector` should not enforece that. | ||
struct SmartVector<T> has store { | ||
inline_inner: vector<T>, | ||
table_inner: vector<BigVector<T>>, |
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.
+1 on adding some quick comments to explain
}; | ||
destroy(v); | ||
} | ||
} |
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.
Can we add more unit tests with edge cases?
3093db5
to
d2bd4a6
Compare
inline_inner: vector<T>, | ||
table_inner: vector<BigVector<T>>, |
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.
first_bucket
and extra_buckets
sound more expressive, just my 2 cents.
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 renamed them as inline_vec and big_vec
vector::push_back(&mut v.inline_inner, val); | ||
return | ||
}; | ||
let estimated_avg_size = max((size_of_val(&v.inline_inner) + val_size) / (inline_len + 1), 1); |
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.
can we generate this estimated_avg_size when creating this smart vector? this value should be the same for every push. doing this once in initialization can save gas
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.
we can take the size of the first element as estimated_avg_size
. But will be very inaccurate for T with variable size. TBH I am not sure which is better.
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.
This looks well suited for specification.
/// `SmartVector` should not enforece that. | ||
struct SmartVector<T> has store { | ||
inline_inner: vector<T>, | ||
table_inner: vector<BigVector<T>>, |
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.
This (or something similar) may help to clarify:
spec struct SmartVector {
invariant vector::length(inline_inner) + vector::length(table_inner) == 1;
}
f74d65e
to
f7320eb
Compare
@msmouse looks good to you now? |
public(friend) fun empty<T: store>(bucket_size: u64): BigVector<T> { | ||
assert!(bucket_size > 0, error::invalid_argument(EZERO_BUCKET_SIZE)); | ||
BigVector { | ||
buckets: table_with_length::new(), | ||
end_index: 0, | ||
num_buckets: 0, | ||
bucket_size, | ||
} | ||
} | ||
|
||
/// Create a vector of length 1 containing the passed in element. | ||
public(friend) fun singleton<T: store>(e: T, bucket_size: u64): BigVector<T> { |
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 made these two friend only to make this struct private for friend modules. Is this the right way to do that? @movekevin
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.
Why do these need to be friend? I.e. why can't these be open for anyone to use?
f7320eb
to
3506067
Compare
move two files to move-examples first for quick adoption. |
const EVECTOR_NOT_EMPTY: u64 = 2; | ||
/// bucket_size cannot be 0 | ||
const EZERO_BUCKET_SIZE: u64 = 3; | ||
|
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.
Now these are values that'll change for an existing module, is that a problem?
Oh.. I guess we didn't publish these example modules on-chain under aptos_std?
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.
It's fine for these to change even if this had been in aptos-stdlib as well. The error mapping would automatically get re-generated when the module is upgraded.
aptos-move/move-examples/data_structures/sources/big_vector.move
Outdated
Show resolved
Hide resolved
aptos-move/move-examples/data_structures/sources/big_vector.move
Outdated
Show resolved
Hide resolved
struct BigVector<T> has store { | ||
buckets: TableWithLength<u64, vector<T>>, | ||
end_index: BigVectorIndex, | ||
end_index: u64, | ||
num_buckets: u64, |
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.
this is redundant because buckets
knows it's 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.
No, buckets know its length but this index is not bucket index but element index.
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 mean num_buckets
@lightmark
aptos-move/move-examples/data_structures/sources/big_vector.move
Outdated
Show resolved
Hide resolved
aptos-move/move-examples/data_structures/sources/big_vector.move
Outdated
Show resolved
Hide resolved
aptos-move/move-examples/data_structures/sources/big_vector.move
Outdated
Show resolved
Hide resolved
aptos-move/move-examples/data_structures/sources/smart_vector.move
Outdated
Show resolved
Hide resolved
aptos-move/move-examples/data_structures/sources/smart_vector.move
Outdated
Show resolved
Hide resolved
aptos-move/move-examples/data_structures/sources/smart_vector.move
Outdated
Show resolved
Hide resolved
vector::swap(table_with_length::borrow_mut(&mut v.buckets, i_bucket_index), i_vector_index, j_vector_index); | ||
return | ||
}; | ||
let bucket_i = table_with_length::remove(&mut v.buckets, i_bucket_index); |
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.
Can you add some comments to explain what this block of code is doing?
3506067
to
b61b520
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.
please remove the framework docs, maybe just a simple cargo build on cached-framework or something. dismiss review when done.
b61b520
to
d887486
Compare
@davidiw done |
10577c9
to
81e076e
Compare
81e076e
to
bc2db41
Compare
bc2db41
to
fb2775e
Compare
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
✅ Forge suite
|
✅ Forge suite
|
Description
This PR productionize big_vector and smart_vector to help move developer handle large sequencing containers on Aptos.
Basically they use
Table
as bucket to batch k elements in one table item.Test Plan
cargo test