Skip to content

Commit

Permalink
[move][stdlib] Add vector::split_off and vector::remove library methods
Browse files Browse the repository at this point in the history
  • Loading branch information
igor-aptos committed Nov 23, 2024
1 parent 32e222b commit 878a3a2
Show file tree
Hide file tree
Showing 14 changed files with 791 additions and 44 deletions.
69 changes: 69 additions & 0 deletions aptos-move/e2e-benchmark/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,15 @@ FungibleAssetMint 10 0.954 1.038 236.3
IncGlobalMilestoneAggV2 { milestone_every: 1 } 10 0.960 1.047 32.9
IncGlobalMilestoneAggV2 { milestone_every: 2 } 10 0.971 1.066 18.1
EmitEvents { count: 1000 } 10 0.969 1.052 8615.5
VectorTrimAppend { vec_len: 3000, element_len: 1, index: 0, repeats: 0 } 6 0.925 1.001 22909
VectorTrimAppend { vec_len: 3000, element_len: 1, index: 100, repeats: 1000 } 6 0.925 1.001 47073
VectorTrimAppend { vec_len: 3000, element_len: 1, index: 2990, repeats: 1000 } 6 0.925 1.001 34788
VectorRemoveInsert { vec_len: 3000, element_len: 1, index: 100, repeats: 1000 } 6 0.925 1.001 45273
VectorRemoveInsert { vec_len: 3000, element_len: 1, index: 2998, repeats: 1000 } 6 0.925 1.001 36859
VectorRangeMove { vec_len: 3000, element_len: 1, index: 1000, move_len: 500, repeats: 1000 } 6 0.925 1.001 65311
VectorTrimAppend { vec_len: 100, element_len: 100, index: 0, repeats: 0 } 6 0.925 1.001 875
VectorTrimAppend { vec_len: 100, element_len: 100, index: 10, repeats: 1000 } 6 0.925 1.001 13366
VectorRangeMove { vec_len: 100, element_len: 100, index: 50, move_len: 10, repeats: 1000 } 6 0.925 1.001 7098
";

struct CalibrationInfo {
Expand Down Expand Up @@ -202,6 +211,66 @@ fn main() {
EntryPoints::IncGlobalMilestoneAggV2 { milestone_every: 1 },
EntryPoints::IncGlobalMilestoneAggV2 { milestone_every: 2 },
EntryPoints::EmitEvents { count: 1000 },
// long vectors with small elements
EntryPoints::VectorTrimAppend {
// baseline, only vector creation
vec_len: 3000,
element_len: 1,
index: 0,
repeats: 0,
},
EntryPoints::VectorTrimAppend {
vec_len: 3000,
element_len: 1,
index: 100,
repeats: 1000,
},
EntryPoints::VectorTrimAppend {
vec_len: 3000,
element_len: 1,
index: 2990,
repeats: 1000,
},
EntryPoints::VectorRemoveInsert {
vec_len: 3000,
element_len: 1,
index: 100,
repeats: 1000,
},
EntryPoints::VectorRemoveInsert {
vec_len: 3000,
element_len: 1,
index: 2998,
repeats: 1000,
},
EntryPoints::VectorRangeMove {
vec_len: 3000,
element_len: 1,
index: 1000,
move_len: 500,
repeats: 1000,
},
// vectors with large elements
EntryPoints::VectorTrimAppend {
// baseline, only vector creation
vec_len: 100,
element_len: 100,
index: 0,
repeats: 0,
},
EntryPoints::VectorTrimAppend {
vec_len: 100,
element_len: 100,
index: 10,
repeats: 1000,
},
EntryPoints::VectorRangeMove {
vec_len: 100,
element_len: 100,
index: 50,
move_len: 10,
repeats: 1000,
},
];

let mut failures = Vec::new();
Expand Down
2 changes: 1 addition & 1 deletion aptos-move/framework/aptos-stdlib/doc/big_vector.md
Original file line number Diff line number Diff line change
Expand Up @@ -502,7 +502,7 @@ Aborts if <code>i</code> is out of bounds.
<b>if</b> (self.end_index == i) {
<b>return</b> last_val
};
// because the lack of mem::swap, here we swap remove the requested value from the bucket
// because the lack of <a href="../../move-stdlib/doc/mem.md#0x1_mem_swap">mem::swap</a>, here we swap remove the requested value from the bucket
// and append the last_val <b>to</b> the bucket then swap the last bucket val back
<b>let</b> bucket = <a href="table_with_length.md#0x1_table_with_length_borrow_mut">table_with_length::borrow_mut</a>(&<b>mut</b> self.buckets, i / self.bucket_size);
<b>let</b> bucket_len = <a href="../../move-stdlib/doc/vector.md#0x1_vector_length">vector::length</a>(bucket);
Expand Down
57 changes: 57 additions & 0 deletions aptos-move/framework/move-stdlib/doc/features.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,8 @@ return true.
- [Function `transaction_simulation_enhancement_enabled`](#0x1_features_transaction_simulation_enhancement_enabled)
- [Function `get_collection_owner_feature`](#0x1_features_get_collection_owner_feature)
- [Function `is_collection_owner_enabled`](#0x1_features_is_collection_owner_enabled)
- [Function `get_native_memory_operations_feature`](#0x1_features_get_native_memory_operations_feature)
- [Function `is_native_memory_operations_enabled`](#0x1_features_is_native_memory_operations_enabled)
- [Function `change_feature_flags`](#0x1_features_change_feature_flags)
- [Function `change_feature_flags_internal`](#0x1_features_change_feature_flags_internal)
- [Function `change_feature_flags_for_next_epoch`](#0x1_features_change_feature_flags_for_next_epoch)
Expand Down Expand Up @@ -671,6 +673,15 @@ Lifetime: transient



<a id="0x1_features_NATIVE_MEMORY_OPERATIONS"></a>



<pre><code><b>const</b> <a href="features.md#0x1_features_NATIVE_MEMORY_OPERATIONS">NATIVE_MEMORY_OPERATIONS</a>: u64 = 80;
</code></pre>



<a id="0x1_features_NEW_ACCOUNTS_DEFAULT_TO_FA_APT_STORE"></a>

Lifetime: transient
Expand Down Expand Up @@ -3274,6 +3285,52 @@ Deprecated feature



</details>

<a id="0x1_features_get_native_memory_operations_feature"></a>

## Function `get_native_memory_operations_feature`



<pre><code><b>public</b> <b>fun</b> <a href="features.md#0x1_features_get_native_memory_operations_feature">get_native_memory_operations_feature</a>(): u64
</code></pre>



<details>
<summary>Implementation</summary>


<pre><code><b>public</b> <b>fun</b> <a href="features.md#0x1_features_get_native_memory_operations_feature">get_native_memory_operations_feature</a>(): u64 { <a href="features.md#0x1_features_NATIVE_MEMORY_OPERATIONS">NATIVE_MEMORY_OPERATIONS</a> }
</code></pre>



</details>

<a id="0x1_features_is_native_memory_operations_enabled"></a>

## Function `is_native_memory_operations_enabled`



<pre><code><b>public</b> <b>fun</b> <a href="features.md#0x1_features_is_native_memory_operations_enabled">is_native_memory_operations_enabled</a>(): bool
</code></pre>



<details>
<summary>Implementation</summary>


<pre><code><b>public</b> <b>fun</b> <a href="features.md#0x1_features_is_native_memory_operations_enabled">is_native_memory_operations_enabled</a>(): bool <b>acquires</b> <a href="features.md#0x1_features_Features">Features</a> {
<a href="features.md#0x1_features_is_enabled">is_enabled</a>(<a href="features.md#0x1_features_NATIVE_MEMORY_OPERATIONS">NATIVE_MEMORY_OPERATIONS</a>)
}
</code></pre>



</details>

<a id="0x1_features_change_feature_flags"></a>
Expand Down
137 changes: 121 additions & 16 deletions aptos-move/framework/move-stdlib/doc/vector.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ the return on investment didn't seem worth it for these simple functions.
- [Function `remove`](#0x1_vector_remove)
- [Function `remove_value`](#0x1_vector_remove_value)
- [Function `swap_remove`](#0x1_vector_swap_remove)
- [Function `replace`](#0x1_vector_replace)
- [Function `for_each`](#0x1_vector_for_each)
- [Function `for_each_reverse`](#0x1_vector_for_each_reverse)
- [Function `for_each_ref`](#0x1_vector_for_each_ref)
Expand Down Expand Up @@ -87,7 +88,8 @@ the return on investment didn't seem worth it for these simple functions.
- [Function `rotate_slice`](#@Specification_1_rotate_slice)


<pre><code></code></pre>
<pre><code><b>use</b> <a href="mem.md#0x1_mem">0x1::mem</a>;
</code></pre>



Expand Down Expand Up @@ -146,6 +148,18 @@ The length of the vectors are not equal.



<a id="0x1_vector_USE_MOVE_RANGE"></a>

Whether to utilize native vector::move_range
Vector module cannot call features module, due to cyclic dependency,
so this is a constant.


<pre><code><b>const</b> <a href="vector.md#0x1_vector_USE_MOVE_RANGE">USE_MOVE_RANGE</a>: bool = <b>true</b>;
</code></pre>



<a id="0x1_vector_empty"></a>

## Function `empty`
Expand Down Expand Up @@ -358,7 +372,7 @@ Move prevents from having two mutable references to the same value, so <code>fro
vectors are always distinct.


<pre><code><b>public</b>(<b>friend</b>) <b>fun</b> <a href="vector.md#0x1_vector_move_range">move_range</a>&lt;T&gt;(from: &<b>mut</b> <a href="vector.md#0x1_vector">vector</a>&lt;T&gt;, removal_position: u64, length: u64, <b>to</b>: &<b>mut</b> <a href="vector.md#0x1_vector">vector</a>&lt;T&gt;, insert_position: u64)
<pre><code><b>public</b> <b>fun</b> <a href="vector.md#0x1_vector_move_range">move_range</a>&lt;T&gt;(from: &<b>mut</b> <a href="vector.md#0x1_vector">vector</a>&lt;T&gt;, removal_position: u64, length: u64, <b>to</b>: &<b>mut</b> <a href="vector.md#0x1_vector">vector</a>&lt;T&gt;, insert_position: u64)
</code></pre>


Expand All @@ -367,7 +381,7 @@ vectors are always distinct.
<summary>Implementation</summary>


<pre><code><b>native</b> <b>public</b>(<b>friend</b>) <b>fun</b> <a href="vector.md#0x1_vector_move_range">move_range</a>&lt;T&gt;(
<pre><code><b>native</b> <b>public</b> <b>fun</b> <a href="vector.md#0x1_vector_move_range">move_range</a>&lt;T&gt;(
from: &<b>mut</b> <a href="vector.md#0x1_vector">vector</a>&lt;T&gt;,
removal_position: u64,
length: u64,
Expand Down Expand Up @@ -482,8 +496,15 @@ Pushes all of the elements of the <code>other</code> vector into the <code>self<


<pre><code><b>public</b> <b>fun</b> <a href="vector.md#0x1_vector_append">append</a>&lt;Element&gt;(self: &<b>mut</b> <a href="vector.md#0x1_vector">vector</a>&lt;Element&gt;, other: <a href="vector.md#0x1_vector">vector</a>&lt;Element&gt;) {
<a href="vector.md#0x1_vector_reverse">reverse</a>(&<b>mut</b> other);
<a href="vector.md#0x1_vector_reverse_append">reverse_append</a>(self, other);
<b>if</b> (<a href="vector.md#0x1_vector_USE_MOVE_RANGE">USE_MOVE_RANGE</a>) {
<b>let</b> self_length = <a href="vector.md#0x1_vector_length">length</a>(self);
<b>let</b> other_length = <a href="vector.md#0x1_vector_length">length</a>(&other);
<a href="vector.md#0x1_vector_move_range">move_range</a>(&<b>mut</b> other, 0, other_length, self, self_length);
<a href="vector.md#0x1_vector_destroy_empty">destroy_empty</a>(other);
} <b>else</b> {
<a href="vector.md#0x1_vector_reverse">reverse</a>(&<b>mut</b> other);
<a href="vector.md#0x1_vector_reverse_append">reverse_append</a>(self, other);
}
}
</code></pre>

Expand Down Expand Up @@ -525,7 +546,11 @@ Pushes all of the elements of the <code>other</code> vector into the <code>self<

## Function `trim`

Trim a vector to a smaller size, returning the evicted elements in order
Splits (trims) the collection into two at the given index.
Returns a newly allocated vector containing the elements in the range [new_len, len).
After the call, the original vector will be left containing the elements [0, new_len)
with its previous capacity unchanged.
In many languages this is also called <code>split_off</code>.


<pre><code><b>public</b> <b>fun</b> <a href="vector.md#0x1_vector_trim">trim</a>&lt;Element&gt;(self: &<b>mut</b> <a href="vector.md#0x1_vector">vector</a>&lt;Element&gt;, new_len: u64): <a href="vector.md#0x1_vector">vector</a>&lt;Element&gt;
Expand All @@ -538,9 +563,21 @@ Trim a vector to a smaller size, returning the evicted elements in order


<pre><code><b>public</b> <b>fun</b> <a href="vector.md#0x1_vector_trim">trim</a>&lt;Element&gt;(self: &<b>mut</b> <a href="vector.md#0x1_vector">vector</a>&lt;Element&gt;, new_len: u64): <a href="vector.md#0x1_vector">vector</a>&lt;Element&gt; {
<b>let</b> res = <a href="vector.md#0x1_vector_trim_reverse">trim_reverse</a>(self, new_len);
<a href="vector.md#0x1_vector_reverse">reverse</a>(&<b>mut</b> res);
res
<b>let</b> len = <a href="vector.md#0x1_vector_length">length</a>(self);
<b>assert</b>!(new_len &lt;= len, <a href="vector.md#0x1_vector_EINDEX_OUT_OF_BOUNDS">EINDEX_OUT_OF_BOUNDS</a>);

<b>let</b> other = <a href="vector.md#0x1_vector_empty">empty</a>();
<b>if</b> (<a href="vector.md#0x1_vector_USE_MOVE_RANGE">USE_MOVE_RANGE</a>) {
<a href="vector.md#0x1_vector_move_range">move_range</a>(self, new_len, len - new_len, &<b>mut</b> other, 0);
} <b>else</b> {
<b>while</b> (len &gt; new_len) {
<a href="vector.md#0x1_vector_push_back">push_back</a>(&<b>mut</b> other, <a href="vector.md#0x1_vector_pop_back">pop_back</a>(self));
len = len - 1;
};
<a href="vector.md#0x1_vector_reverse">reverse</a>(&<b>mut</b> other);
};

other
}
</code></pre>

Expand Down Expand Up @@ -728,10 +765,27 @@ Aborts if out of bounds.
<pre><code><b>public</b> <b>fun</b> <a href="vector.md#0x1_vector_insert">insert</a>&lt;Element&gt;(self: &<b>mut</b> <a href="vector.md#0x1_vector">vector</a>&lt;Element&gt;, i: u64, e: Element) {
<b>let</b> len = <a href="vector.md#0x1_vector_length">length</a>(self);
<b>assert</b>!(i &lt;= len, <a href="vector.md#0x1_vector_EINDEX_OUT_OF_BOUNDS">EINDEX_OUT_OF_BOUNDS</a>);
<a href="vector.md#0x1_vector_push_back">push_back</a>(self, e);
<b>while</b> (i &lt; len) {
<a href="vector.md#0x1_vector_swap">swap</a>(self, i, len);
i = i + 1;

<b>if</b> (<a href="vector.md#0x1_vector_USE_MOVE_RANGE">USE_MOVE_RANGE</a>) {
<b>if</b> (i + 2 &gt;= len) {
// When we are close <b>to</b> the end, it is cheaper <b>to</b> not create
// a temporary <a href="vector.md#0x1_vector">vector</a>, and swap directly
<a href="vector.md#0x1_vector_push_back">push_back</a>(self, e);
<b>while</b> (i &lt; len) {
<a href="vector.md#0x1_vector_swap">swap</a>(self, i, len);
i = i + 1;
};
} <b>else</b> {
<b>let</b> other = <a href="vector.md#0x1_vector_singleton">singleton</a>(e);
<a href="vector.md#0x1_vector_move_range">move_range</a>(&<b>mut</b> other, 0, 1, self, i);
<a href="vector.md#0x1_vector_destroy_empty">destroy_empty</a>(other);
}
} <b>else</b> {
<a href="vector.md#0x1_vector_push_back">push_back</a>(self, e);
<b>while</b> (i &lt; len) {
<a href="vector.md#0x1_vector_swap">swap</a>(self, i, len);
i = i + 1;
};
};
}
</code></pre>
Expand Down Expand Up @@ -763,9 +817,25 @@ Aborts if <code>i</code> is out of bounds.
// i out of bounds; <b>abort</b>
<b>if</b> (i &gt;= len) <b>abort</b> <a href="vector.md#0x1_vector_EINDEX_OUT_OF_BOUNDS">EINDEX_OUT_OF_BOUNDS</a>;

len = len - 1;
<b>while</b> (i &lt; len) <a href="vector.md#0x1_vector_swap">swap</a>(self, i, { i = i + 1; i });
<a href="vector.md#0x1_vector_pop_back">pop_back</a>(self)
<b>if</b> (<a href="vector.md#0x1_vector_USE_MOVE_RANGE">USE_MOVE_RANGE</a>) {
// When we are close <b>to</b> the end, it is cheaper <b>to</b> not create
// a temporary <a href="vector.md#0x1_vector">vector</a>, and swap directly
<b>if</b> (i + 3 &gt;= len) {
len = len - 1;
<b>while</b> (i &lt; len) <a href="vector.md#0x1_vector_swap">swap</a>(self, i, { i = i + 1; i });
<a href="vector.md#0x1_vector_pop_back">pop_back</a>(self)
} <b>else</b> {
<b>let</b> other = <a href="vector.md#0x1_vector_empty">empty</a>();
<a href="vector.md#0x1_vector_move_range">move_range</a>(self, i, 1, &<b>mut</b> other, 0);
<b>let</b> result = <a href="vector.md#0x1_vector_pop_back">pop_back</a>(&<b>mut</b> other);
<a href="vector.md#0x1_vector_destroy_empty">destroy_empty</a>(other);
result
}
} <b>else</b> {
len = len - 1;
<b>while</b> (i &lt; len) <a href="vector.md#0x1_vector_swap">swap</a>(self, i, { i = i + 1; i });
<a href="vector.md#0x1_vector_pop_back">pop_back</a>(self)
}
}
</code></pre>

Expand Down Expand Up @@ -838,6 +908,41 @@ Aborts if <code>i</code> is out of bounds.



</details>

<a id="0x1_vector_replace"></a>

## Function `replace`

Replace the <code>i</code>th element of the vector <code>self</code> with the given value, and return
to the caller the value that was there before.
Aborts if <code>i</code> is out of bounds.


<pre><code><b>public</b> <b>fun</b> <a href="vector.md#0x1_vector_replace">replace</a>&lt;Element&gt;(self: &<b>mut</b> <a href="vector.md#0x1_vector">vector</a>&lt;Element&gt;, i: u64, val: Element): Element
</code></pre>



<details>
<summary>Implementation</summary>


<pre><code><b>public</b> <b>fun</b> <a href="vector.md#0x1_vector_replace">replace</a>&lt;Element&gt;(self: &<b>mut</b> <a href="vector.md#0x1_vector">vector</a>&lt;Element&gt;, i: u64, val: Element): Element {
<b>let</b> last_idx = <a href="vector.md#0x1_vector_length">length</a>(self);
<b>assert</b>!(i &lt; last_idx, <a href="vector.md#0x1_vector_EINDEX_OUT_OF_BOUNDS">EINDEX_OUT_OF_BOUNDS</a>);
<b>if</b> (<a href="vector.md#0x1_vector_USE_MOVE_RANGE">USE_MOVE_RANGE</a>) {
<a href="mem.md#0x1_mem_replace">mem::replace</a>(<a href="vector.md#0x1_vector_borrow_mut">borrow_mut</a>(self, i), val)
} <b>else</b> {
<a href="vector.md#0x1_vector_push_back">push_back</a>(self, val);
<a href="vector.md#0x1_vector_swap">swap</a>(self, i, last_idx);
<a href="vector.md#0x1_vector_pop_back">pop_back</a>(self)
}
}
</code></pre>



</details>

<a id="0x1_vector_for_each"></a>
Expand Down
Loading

0 comments on commit 878a3a2

Please sign in to comment.