From 5902ff090d9ba38a1da57ae9bc32a0b2cff01ad2 Mon Sep 17 00:00:00 2001 From: igor-aptos <110557261+igor-aptos@users.noreply.github.com> Date: Thu, 5 Dec 2024 18:01:01 -0800 Subject: [PATCH] [move-stdlib] Use vector::move_range inside vector, and evaluate performance / calibrate gas (#14862) ## Description Use vector::move_range inside of vector, to optimize `insert`, `remove`, `append`, `trim`. Extend aptos-move/e2e-benchmark/src/main.rs to track gas and gas/s, to allow for quick calibration. Adding workloads to txn-emitter to be able to use it throughput. Additionally add a missing `replace` method, which replaces value at particular index. Running on extended set of params: https://gist.github.com/igor-aptos/e8d4e21edcbc75dddcb9382d4e077665 Summary of the performance tests: - performance doesn't depend on the size of the values (unless they are primitive), as vector modifies only pointers to them. - operation depends very little on how many elements we need to move - moving 1000 elements (i.e. to insert into a vector 1000 elements from the end) is only (!!) 2x slower than moving 1 element, end-to-end. For gas calibration, on the variety of workloads, current implementation has decent variance. After tuning params to match the averages, variance seems much smaller. --- .../e2e-benchmark/data/calibration_values.tsv | 9 + aptos-move/e2e-benchmark/src/main.rs | 60 ++++++ .../framework/move-stdlib/doc/features.md | 57 +++++ .../framework/move-stdlib/doc/vector.md | 131 ++++++++++-- .../framework/move-stdlib/doc/vector_ext.md | 201 ++++++++++++++++++ .../move-stdlib/sources/configs/features.move | 8 + .../framework/move-stdlib/sources/mem.move | 1 + .../framework/move-stdlib/sources/vector.move | 105 +++++++-- .../move-stdlib/tests/vector_tests.move | 129 ++++++++++- crates/transaction-generator-lib/src/args.rs | 18 ++ .../src/publishing/module_simple.rs | 73 +++++++ .../src/publishing/raw_module_data.rs | 62 +++++- .../sources/vector_example.move | 47 ++++ testsuite/single_node_performance.py | 3 + testsuite/single_node_performance_values.tsv | 2 + 15 files changed, 870 insertions(+), 36 deletions(-) create mode 100644 aptos-move/framework/move-stdlib/doc/vector_ext.md create mode 100644 testsuite/module-publish/src/packages/framework_usecases/sources/vector_example.move diff --git a/aptos-move/e2e-benchmark/data/calibration_values.tsv b/aptos-move/e2e-benchmark/data/calibration_values.tsv index 0c373cd15b35f..f8f8281e3b772 100644 --- a/aptos-move/e2e-benchmark/data/calibration_values.tsv +++ b/aptos-move/e2e-benchmark/data/calibration_values.tsv @@ -24,3 +24,12 @@ FungibleAssetMint 60 0.930 1.098 235.8 IncGlobalMilestoneAggV2 { milestone_every: 1 } 60 0.914 1.051 33.5 IncGlobalMilestoneAggV2 { milestone_every: 2 } 60 0.914 1.105 19.0 EmitEvents { count: 1000 } 60 0.937 1.158 8818.7 +VectorTrimAppend { vec_len: 3000, element_len: 1, index: 0, repeats: 0 } 6 0.925 1.001 6058.1 +VectorTrimAppend { vec_len: 3000, element_len: 1, index: 100, repeats: 1000 } 6 0.925 1.001 34000.0 +VectorTrimAppend { vec_len: 3000, element_len: 1, index: 2990, repeats: 1000 } 6 0.925 1.001 17626.5 +VectorRemoveInsert { vec_len: 3000, element_len: 1, index: 100, repeats: 1000 } 6 0.925 1.001 30870.3 +VectorRemoveInsert { vec_len: 3000, element_len: 1, index: 2998, repeats: 1000 } 6 0.925 1.001 20343.2 +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 277.0 +VectorTrimAppend { vec_len: 100, element_len: 100, index: 10, repeats: 1000 } 6 0.925 1.001 12146.6 +VectorRangeMove { vec_len: 100, element_len: 100, index: 50, move_len: 10, repeats: 1000 } 6 0.925 1.001 7098 \ No newline at end of file diff --git a/aptos-move/e2e-benchmark/src/main.rs b/aptos-move/e2e-benchmark/src/main.rs index fd6365284d56d..fd343e1ff15b7 100644 --- a/aptos-move/e2e-benchmark/src/main.rs +++ b/aptos-move/e2e-benchmark/src/main.rs @@ -176,6 +176,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(); diff --git a/aptos-move/framework/move-stdlib/doc/features.md b/aptos-move/framework/move-stdlib/doc/features.md index 3b9ad226d2e09..ba635d008abad 100644 --- a/aptos-move/framework/move-stdlib/doc/features.md +++ b/aptos-move/framework/move-stdlib/doc/features.md @@ -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) @@ -671,6 +673,15 @@ Lifetime: transient + + + + +
const NATIVE_MEMORY_OPERATIONS: u64 = 80;
+
+
+
+
Lifetime: transient
@@ -3274,6 +3285,52 @@ Deprecated feature
+
+
+
+
+## Function `get_native_memory_operations_feature`
+
+
+
+public fun get_native_memory_operations_feature(): u64
+
+
+
+
+public fun get_native_memory_operations_feature(): u64 { NATIVE_MEMORY_OPERATIONS }
+
+
+
+
+public fun is_native_memory_operations_enabled(): bool
+
+
+
+
+public fun is_native_memory_operations_enabled(): bool acquires Features {
+ is_enabled(NATIVE_MEMORY_OPERATIONS)
+}
+
+
+
+
const USE_MOVE_RANGE: bool = true;
+
+
+
+
## Function `empty`
@@ -482,8 +495,15 @@ Pushes all of the elements of the other
vector into the self<
public fun append<Element>(self: &mut vector<Element>, other: vector<Element>) {
- reverse(&mut other);
- reverse_append(self, other);
+ if (USE_MOVE_RANGE) {
+ let self_length = length(self);
+ let other_length = length(&other);
+ move_range(&mut other, 0, other_length, self, self_length);
+ destroy_empty(other);
+ } else {
+ reverse(&mut other);
+ reverse_append(self, other);
+ }
}
@@ -525,7 +545,11 @@ Pushes all of the elements of the other
vector into the 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 split_off
.
public fun trim<Element>(self: &mut vector<Element>, new_len: u64): vector<Element>
@@ -538,9 +562,21 @@ Trim a vector to a smaller size, returning the evicted elements in order
public fun trim<Element>(self: &mut vector<Element>, new_len: u64): vector<Element> {
- let res = trim_reverse(self, new_len);
- reverse(&mut res);
- res
+ let len = length(self);
+ assert!(new_len <= len, EINDEX_OUT_OF_BOUNDS);
+
+ let other = empty();
+ if (USE_MOVE_RANGE) {
+ move_range(self, new_len, len - new_len, &mut other, 0);
+ } else {
+ while (len > new_len) {
+ push_back(&mut other, pop_back(self));
+ len = len - 1;
+ };
+ reverse(&mut other);
+ };
+
+ other
}
@@ -728,10 +764,27 @@ Aborts if out of bounds.
public fun insert<Element>(self: &mut vector<Element>, i: u64, e: Element) {
let len = length(self);
assert!(i <= len, EINDEX_OUT_OF_BOUNDS);
- push_back(self, e);
- while (i < len) {
- swap(self, i, len);
- i = i + 1;
+
+ if (USE_MOVE_RANGE) {
+ if (i + 2 >= len) {
+ // When we are close to the end, it is cheaper to not create
+ // a temporary vector, and swap directly
+ push_back(self, e);
+ while (i < len) {
+ swap(self, i, len);
+ i = i + 1;
+ };
+ } else {
+ let other = singleton(e);
+ move_range(&mut other, 0, 1, self, i);
+ destroy_empty(other);
+ }
+ } else {
+ push_back(self, e);
+ while (i < len) {
+ swap(self, i, len);
+ i = i + 1;
+ };
};
}
@@ -763,9 +816,25 @@ Aborts if i
is out of bounds.
// i out of bounds; abort
if (i >= len) abort EINDEX_OUT_OF_BOUNDS;
- len = len - 1;
- while (i < len) swap(self, i, { i = i + 1; i });
- pop_back(self)
+ if (USE_MOVE_RANGE) {
+ // When we are close to the end, it is cheaper to not create
+ // a temporary vector, and swap directly
+ if (i + 3 >= len) {
+ len = len - 1;
+ while (i < len) swap(self, i, { i = i + 1; i });
+ pop_back(self)
+ } else {
+ let other = empty();
+ move_range(self, i, 1, &mut other, 0);
+ let result = pop_back(&mut other);
+ destroy_empty(other);
+ result
+ }
+ } else {
+ len = len - 1;
+ while (i < len) swap(self, i, { i = i + 1; i });
+ pop_back(self)
+ }
}
@@ -838,6 +907,42 @@ Aborts if i
is out of bounds.
+
+
+
+
+## Function `replace`
+
+Replace the i
th element of the vector self
with the given value, and return
+to the caller the value that was there before.
+Aborts if i
is out of bounds.
+
+
+public fun replace<Element>(self: &mut vector<Element>, i: u64, val: Element): Element
+
+
+
+
+
+Implementation
+
+
+public fun replace<Element>(self: &mut vector<Element>, i: u64, val: Element): Element {
+ let last_idx = length(self);
+ assert!(i < last_idx, EINDEX_OUT_OF_BOUNDS);
+ // TODO: Enable after tests are fixed.
+ // if (USE_MOVE_RANGE) {
+ // mem::replace(borrow_mut(self, i), val)
+ // } else {
+ push_back(self, val);
+ swap(self, i, last_idx);
+ pop_back(self)
+ // }
+}
+
+
+
+
diff --git a/aptos-move/framework/move-stdlib/doc/vector_ext.md b/aptos-move/framework/move-stdlib/doc/vector_ext.md
new file mode 100644
index 0000000000000..1fa207f777813
--- /dev/null
+++ b/aptos-move/framework/move-stdlib/doc/vector_ext.md
@@ -0,0 +1,201 @@
+
+
+
+# Module `0x1::vector_ext`
+
+
+
+- [Constants](#@Constants_0)
+- [Function `range_move`](#0x1_vector_ext_range_move)
+- [Function `split_off`](#0x1_vector_ext_split_off)
+- [Function `append`](#0x1_vector_ext_append)
+- [Function `insert`](#0x1_vector_ext_insert)
+- [Function `remove`](#0x1_vector_ext_remove)
+
+
+use 0x1::vector;
+
+
+
+
+
+
+## Constants
+
+
+
+
+The index into the vector is out of bounds
+
+
+const EINDEX_OUT_OF_BOUNDS: u64 = 131072;
+
+
+
+
+
+
+## Function `range_move`
+
+
+
+public fun range_move<T>(from: &mut vector<T>, removal_position: u64, length: u64, to: &mut vector<T>, insert_position: u64)
+
+
+
+
+
+Implementation
+
+
+public native fun range_move<T>(from: &mut vector<T>, removal_position: u64, length: u64, to: &mut vector<T>, insert_position: u64);
+
+
+
+
+
+
+
+
+## Function `split_off`
+
+Splits the collection into two at the given index.
+Returns a newly allocated vector containing the elements in the range [at, len).
+After the call, the original vector will be left containing the elements [0, at)
+with its previous capacity unchanged.
+
+
+public fun split_off<Element>(self: &mut vector<Element>, at: u64): vector<Element>
+
+
+
+
+
+Implementation
+
+
+public fun split_off<Element>(self: &mut vector<Element>, at: u64): vector<Element> {
+ let len = vector::length(self);
+ assert!(at <= len, EINDEX_OUT_OF_BOUNDS);
+
+ let other = vector::empty();
+ range_move(self, at, len - at, &mut other, 0);
+
+ // let other = empty();
+ // while (len > at) {
+ // push_back(&mut other, pop_back(self));
+ // len = len - 1;
+ // };
+ // reverse(&mut other);
+ other
+}
+
+
+
+
+
+
+
+
+## Function `append`
+
+Pushes all of the elements of the other
vector into the self
vector.
+
+
+public fun append<Element>(self: &mut vector<Element>, other: vector<Element>)
+
+
+
+
+
+Implementation
+
+
+public fun append<Element>(self: &mut vector<Element>, other: vector<Element>) {
+ let self_length = self.length();
+ let other_length = other.length();
+ range_move(&mut other, 0, other_length, self, self_length);
+ other.destroy_empty();
+ // reverse(&mut other);
+ // reverse_append(self, other);
+}
+
+
+
+
+
+
+
+
+## Function `insert`
+
+
+
+public fun insert<Element>(self: &mut vector<Element>, i: u64, e: Element)
+
+
+
+
+
+Implementation
+
+
+public fun insert<Element>(self: &mut vector<Element>, i: u64, e: Element) {
+ let len = self.length();
+ assert!(i <= len, EINDEX_OUT_OF_BOUNDS);
+
+ if (i == len) {
+ self.push_back(e);
+ } else {
+ let other = vector::singleton(e);
+ range_move(&mut other, 0, 1, self, i);
+ other.destroy_empty();
+ }
+}
+
+
+
+
+
+
+
+
+## Function `remove`
+
+Remove the i
th element of the vector self
, shifting all subsequent elements.
+This is O(n) and preserves ordering of elements in the vector.
+Aborts if i
is out of bounds.
+
+
+public fun remove<Element>(self: &mut vector<Element>, i: u64): Element
+
+
+
+
+
+Implementation
+
+
+public fun remove<Element>(self: &mut vector<Element>, i: u64): Element {
+ let len = self.length();
+ // i out of bounds; abort
+ if (i >= len) abort EINDEX_OUT_OF_BOUNDS;
+
+ if (i + 1 == len) {
+ self.pop_back()
+ } else {
+ let other = vector::empty();
+ range_move(self, i, 1, &mut other, 0);
+ let result = other.pop_back();
+ other.destroy_empty();
+ result
+ }
+}
+
+
+
+
+
+
+
+[move-book]: https://aptos.dev/move/book/SUMMARY
diff --git a/aptos-move/framework/move-stdlib/sources/configs/features.move b/aptos-move/framework/move-stdlib/sources/configs/features.move
index 2bdba4056eaae..2b3a5291c600d 100644
--- a/aptos-move/framework/move-stdlib/sources/configs/features.move
+++ b/aptos-move/framework/move-stdlib/sources/configs/features.move
@@ -607,6 +607,14 @@ module std::features {
is_enabled(COLLECTION_OWNER)
}
+ const NATIVE_MEMORY_OPERATIONS: u64 = 80;
+
+ public fun get_native_memory_operations_feature(): u64 { NATIVE_MEMORY_OPERATIONS }
+
+ public fun is_native_memory_operations_enabled(): bool acquires Features {
+ is_enabled(NATIVE_MEMORY_OPERATIONS)
+ }
+
// ============================================================================================
// Feature Flag Implementation
diff --git a/aptos-move/framework/move-stdlib/sources/mem.move b/aptos-move/framework/move-stdlib/sources/mem.move
index 424766f4a2124..cf2eae276f9a5 100644
--- a/aptos-move/framework/move-stdlib/sources/mem.move
+++ b/aptos-move/framework/move-stdlib/sources/mem.move
@@ -2,6 +2,7 @@
module std::mem {
// TODO - functions here are `public(friend)` here for one release,
// and to be changed to `public` one release later.
+ // friend std::vector;
#[test_only]
friend std::mem_tests;
diff --git a/aptos-move/framework/move-stdlib/sources/vector.move b/aptos-move/framework/move-stdlib/sources/vector.move
index 2d67042ef4fef..b7add1eb2112a 100644
--- a/aptos-move/framework/move-stdlib/sources/vector.move
+++ b/aptos-move/framework/move-stdlib/sources/vector.move
@@ -9,6 +9,8 @@
/// Move functions here because many have loops, requiring loop invariants to prove, and
/// the return on investment didn't seem worth it for these simple functions.
module std::vector {
+ // use std::mem;
+
/// The index into the vector is out of bounds
const EINDEX_OUT_OF_BOUNDS: u64 = 0x20000;
@@ -24,6 +26,11 @@ module std::vector {
/// The range in `slice` is invalid.
const EINVALID_SLICE_RANGE: u64 = 0x20004;
+ /// Whether to utilize native vector::move_range
+ /// Vector module cannot call features module, due to cyclic dependency,
+ /// so this is a constant.
+ const USE_MOVE_RANGE: bool = true;
+
#[bytecode_instruction]
/// Create an empty vector.
native public fun empty(): vector;
@@ -121,8 +128,15 @@ module std::vector {
/// Pushes all of the elements of the `other` vector into the `self` vector.
public fun append(self: &mut vector, other: vector) {
- reverse(&mut other);
- reverse_append(self, other);
+ if (USE_MOVE_RANGE) {
+ let self_length = length(self);
+ let other_length = length(&other);
+ move_range(&mut other, 0, other_length, self, self_length);
+ destroy_empty(other);
+ } else {
+ reverse(&mut other);
+ reverse_append(self, other);
+ }
}
spec append {
pragma intrinsic = true;
@@ -144,11 +158,27 @@ module std::vector {
pragma intrinsic = true;
}
- /// 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 `split_off`.
public fun trim(self: &mut vector, new_len: u64): vector {
- let res = trim_reverse(self, new_len);
- reverse(&mut res);
- res
+ let len = length(self);
+ assert!(new_len <= len, EINDEX_OUT_OF_BOUNDS);
+
+ let other = empty();
+ if (USE_MOVE_RANGE) {
+ move_range(self, new_len, len - new_len, &mut other, 0);
+ } else {
+ while (len > new_len) {
+ push_back(&mut other, pop_back(self));
+ len = len - 1;
+ };
+ reverse(&mut other);
+ };
+
+ other
}
spec trim {
pragma intrinsic = true;
@@ -229,10 +259,27 @@ module std::vector {
public fun insert(self: &mut vector, i: u64, e: Element) {
let len = length(self);
assert!(i <= len, EINDEX_OUT_OF_BOUNDS);
- push_back(self, e);
- while (i < len) {
- swap(self, i, len);
- i = i + 1;
+
+ if (USE_MOVE_RANGE) {
+ if (i + 2 >= len) {
+ // When we are close to the end, it is cheaper to not create
+ // a temporary vector, and swap directly
+ push_back(self, e);
+ while (i < len) {
+ swap(self, i, len);
+ i = i + 1;
+ };
+ } else {
+ let other = singleton(e);
+ move_range(&mut other, 0, 1, self, i);
+ destroy_empty(other);
+ }
+ } else {
+ push_back(self, e);
+ while (i < len) {
+ swap(self, i, len);
+ i = i + 1;
+ };
};
}
spec insert {
@@ -247,9 +294,25 @@ module std::vector {
// i out of bounds; abort
if (i >= len) abort EINDEX_OUT_OF_BOUNDS;
- len = len - 1;
- while (i < len) swap(self, i, { i = i + 1; i });
- pop_back(self)
+ if (USE_MOVE_RANGE) {
+ // When we are close to the end, it is cheaper to not create
+ // a temporary vector, and swap directly
+ if (i + 3 >= len) {
+ len = len - 1;
+ while (i < len) swap(self, i, { i = i + 1; i });
+ pop_back(self)
+ } else {
+ let other = empty();
+ move_range(self, i, 1, &mut other, 0);
+ let result = pop_back(&mut other);
+ destroy_empty(other);
+ result
+ }
+ } else {
+ len = len - 1;
+ while (i < len) swap(self, i, { i = i + 1; i });
+ pop_back(self)
+ }
}
spec remove {
pragma intrinsic = true;
@@ -288,6 +351,22 @@ module std::vector {
pragma intrinsic = true;
}
+ /// Replace the `i`th element of the vector `self` with the given value, and return
+ /// to the caller the value that was there before.
+ /// Aborts if `i` is out of bounds.
+ public fun replace(self: &mut vector, i: u64, val: Element): Element {
+ let last_idx = length(self);
+ assert!(i < last_idx, EINDEX_OUT_OF_BOUNDS);
+ // TODO: Enable after tests are fixed.
+ // if (USE_MOVE_RANGE) {
+ // mem::replace(borrow_mut(self, i), val)
+ // } else {
+ push_back(self, val);
+ swap(self, i, last_idx);
+ pop_back(self)
+ // }
+ }
+
/// Apply the function to each element in the vector, consuming it.
public inline fun for_each(self: vector, f: |Element|) {
reverse(&mut self); // We need to reverse the vector to consume it efficiently
diff --git a/aptos-move/framework/move-stdlib/tests/vector_tests.move b/aptos-move/framework/move-stdlib/tests/vector_tests.move
index ce558bc3e9cfc..c01674a6dd4ff 100644
--- a/aptos-move/framework/move-stdlib/tests/vector_tests.move
+++ b/aptos-move/framework/move-stdlib/tests/vector_tests.move
@@ -105,7 +105,18 @@ module std::vector_tests {
let v = vector[1, 2];
assert!(&V::trim(&mut v, 0) == &vector[1, 2], 3);
};
+ {
+ let v = vector[1, 2, 3, 4, 5, 6];
+ let other = V::trim(&mut v, 4);
+ assert!(v == vector[1, 2, 3, 4], 4);
+ assert!(other == vector[5, 6], 5);
+
+ let other_empty = V::trim(&mut v, 4);
+ assert!(v == vector[1, 2, 3, 4], 6);
+ assert!(other_empty == vector[], 7);
+ };
}
+
#[test]
#[expected_failure(abort_code = V::EINDEX_OUT_OF_BOUNDS)]
fun test_trim_fail() {
@@ -113,6 +124,13 @@ module std::vector_tests {
V::trim(&mut v, 2);
}
+ #[test]
+ #[expected_failure(abort_code = V::EINDEX_OUT_OF_BOUNDS)]
+ fun test_trim_fail_2() {
+ let v = vector[1, 2, 3];
+ V::trim(&mut v, 4);
+ }
+
#[test]
#[expected_failure(vector_error, minor_status = 1, location = Self)]
fun borrow_out_of_range() {
@@ -266,6 +284,52 @@ module std::vector_tests {
V::remove(&mut v, 1);
}
+ fun remove_more_cases() {
+ let v :vector = vector[1];
+ assert!(V::remove(&mut v, 0) == 1, 1);
+ assert!(&v == &vector[], 1);
+
+ let v :vector = vector[2, 1];
+ assert!(V::remove(&mut v, 0) == 2, 1);
+ assert!(&v == &vector[1], 1);
+
+ let v :vector = vector[1, 2];
+ assert!(V::remove(&mut v, 1) == 2, 1);
+ assert!(&v == &vector[1], 1);
+
+ let v :vector = vector[3, 1, 2];
+ assert!(V::remove(&mut v, 0) == 3, 1);
+ assert!(&v == &vector[1, 2], 1);
+
+ let v :vector = vector[1, 3, 2];
+ assert!(V::remove(&mut v, 1) == 3, 1);
+ assert!(&v == &vector[1, 2], 1);
+
+ let v :vector = vector[1, 2, 3];
+ assert!(V::remove(&mut v, 2) == 3, 1);
+ assert!(&v == &vector[1, 2], 1);
+
+ let v :vector = vector[4, 1, 2, 3];
+ assert!(V::remove(&mut v, 0) == 4, 1);
+ assert!(&v == &vector[1, 2, 3], 1);
+
+ let v :vector = vector[5, 1, 2, 3, 4];
+ assert!(V::remove(&mut v, 0) == 5, 1);
+ assert!(&v == &vector[1, 2, 3, 4], 1);
+
+ let v :vector = vector[1, 5, 2, 3, 4];
+ assert!(V::remove(&mut v, 1) == 5, 1);
+ assert!(&v == &vector[1, 2, 3, 4], 1);
+
+ let v :vector = vector[1, 2, 5, 3, 4];
+ assert!(V::remove(&mut v, 2) == 5, 1);
+ assert!(&v == &vector[1, 2, 3, 4], 1);
+
+ let v :vector = vector[1, 2, 3, 4, 5];
+ assert!(V::remove(&mut v, 4) == 5, 1);
+ assert!(&v == &vector[1, 2, 3, 4], 1);
+ }
+
#[test]
fun remove_value_singleton_vector() {
let v = V::empty();
@@ -873,11 +937,55 @@ module std::vector_tests {
fun test_insert() {
let v:vector = vector[1, 2, 3, 4, 5];
- vector::insert(&mut v,2, 6);
+ V::insert(&mut v,2, 6);
assert!(&v == &vector[1, 2, 6, 3, 4, 5], 1);
- vector::insert(&mut v,6, 7);
+ V::insert(&mut v,6, 7);
assert!(&v == &vector[1, 2, 6, 3, 4, 5, 7], 1);
+
+ let v :vector = vector[];
+ V::insert(&mut v, 0, 1);
+ assert!(&v == &vector[1], 1);
+
+ let v :vector = vector[1];
+ V::insert(&mut v, 0, 2);
+ assert!(&v == &vector[2, 1], 1);
+
+ let v :vector = vector[1];
+ V::insert(&mut v, 1, 2);
+ assert!(&v == &vector[1, 2], 1);
+
+ let v :vector = vector[1, 2];
+ V::insert(&mut v, 0, 3);
+ assert!(&v == &vector[3, 1, 2], 1);
+
+ let v :vector = vector[1, 2];
+ V::insert(&mut v, 1, 3);
+ assert!(&v == &vector[1, 3, 2], 1);
+
+ let v :vector = vector[1, 2];
+ V::insert(&mut v, 2, 3);
+ assert!(&v == &vector[1, 2, 3], 1);
+
+ let v :vector = vector[1, 2, 3];
+ V::insert(&mut v, 0, 4);
+ assert!(&v == &vector[4, 1, 2, 3], 1);
+
+ let v :vector = vector[1, 2, 3, 4];
+ V::insert(&mut v, 0, 5);
+ assert!(&v == &vector[5, 1, 2, 3, 4], 1);
+
+ let v :vector = vector[1, 2, 3, 4];
+ V::insert(&mut v, 1, 5);
+ assert!(&v == &vector[1, 5, 2, 3, 4], 1);
+
+ let v :vector = vector[1, 2, 3, 4];
+ V::insert(&mut v, 2, 5);
+ assert!(&v == &vector[1, 2, 5, 3, 4], 1);
+
+ let v :vector = vector[1, 2, 3, 4];
+ V::insert(&mut v, 4, 5);
+ assert!(&v == &vector[1, 2, 3, 4, 5], 1);
}
#[test]
@@ -952,7 +1060,7 @@ module std::vector_tests {
#[test]
fun test_destroy() {
let v = vector[MoveOnly {}];
- vector::destroy(v, |m| { let MoveOnly {} = m; })
+ V::destroy(v, |m| { let MoveOnly {} = m; })
}
#[test]
@@ -964,4 +1072,19 @@ module std::vector_tests {
assert!(&v == &vector[3, 6], 0);
assert!(&w == &vector[1, 4, 5, 2], 0);
}
+
+ #[test]
+ #[expected_failure(abort_code = V::EINDEX_OUT_OF_BOUNDS)]
+ fun test_replace_empty_abort() {
+ let v = vector[];
+ let MoveOnly {} = V::replace(&mut v, 0, MoveOnly {});
+ V::destroy_empty(v);
+ }
+
+ #[test]
+ fun test_replace() {
+ let v = vector[1, 2, 3, 4];
+ V::replace(&mut v, 1, 17);
+ assert!(v == vector[1, 17, 3, 4], 0);
+ }
}
diff --git a/crates/transaction-generator-lib/src/args.rs b/crates/transaction-generator-lib/src/args.rs
index fb0cc7779d427..da2a5e5a5f385 100644
--- a/crates/transaction-generator-lib/src/args.rs
+++ b/crates/transaction-generator-lib/src/args.rs
@@ -45,6 +45,8 @@ pub enum TransactionTypeArg {
CreateObjects100,
CreateObjects100WithPayload10k,
CreateObjectsConflict100WithPayload10k,
+ VectorTrimAppendLen3000Size1,
+ VectorRemoveInsertLen3000Size1,
ResourceGroupsGlobalWriteTag1KB,
ResourceGroupsGlobalWriteAndReadTag1KB,
ResourceGroupsSenderWriteTag1KB,
@@ -222,6 +224,22 @@ impl TransactionTypeArg {
object_payload_size: 10 * 1024,
})
},
+ TransactionTypeArg::VectorTrimAppendLen3000Size1 => {
+ call_custom_module(EntryPoints::VectorTrimAppend {
+ vec_len: 3000,
+ element_len: 1,
+ index: 100,
+ repeats: 1000,
+ })
+ },
+ TransactionTypeArg::VectorRemoveInsertLen3000Size1 => {
+ call_custom_module(EntryPoints::VectorRemoveInsert {
+ vec_len: 3000,
+ element_len: 1,
+ index: 100,
+ repeats: 1000,
+ })
+ },
TransactionTypeArg::ResourceGroupsGlobalWriteTag1KB => {
call_custom_module(EntryPoints::ResourceGroupsGlobalWriteTag {
string_length: 1024,
diff --git a/crates/transaction-generator-lib/src/publishing/module_simple.rs b/crates/transaction-generator-lib/src/publishing/module_simple.rs
index f1b338bb23a16..de1ed2776c96a 100644
--- a/crates/transaction-generator-lib/src/publishing/module_simple.rs
+++ b/crates/transaction-generator-lib/src/publishing/module_simple.rs
@@ -229,6 +229,25 @@ pub enum EntryPoints {
num_objects: u64,
object_payload_size: u64,
},
+ VectorTrimAppend {
+ vec_len: u64,
+ element_len: u64,
+ index: u64,
+ repeats: u64,
+ },
+ VectorRemoveInsert {
+ vec_len: u64,
+ element_len: u64,
+ index: u64,
+ repeats: u64,
+ },
+ VectorRangeMove {
+ vec_len: u64,
+ element_len: u64,
+ index: u64,
+ move_len: u64,
+ repeats: u64,
+ },
/// Initialize Token V1 NFT collection
TokenV1InitializeCollection,
/// Mint an NFT token. Should be called only after InitializeCollection is called
@@ -306,6 +325,9 @@ impl EntryPoints {
| EntryPoints::ModifyGlobalBoundedAggV2 { .. }
| EntryPoints::CreateObjects { .. }
| EntryPoints::CreateObjectsConflict { .. }
+ | EntryPoints::VectorTrimAppend { .. }
+ | EntryPoints::VectorRemoveInsert { .. }
+ | EntryPoints::VectorRangeMove { .. }
| EntryPoints::TokenV1InitializeCollection
| EntryPoints::TokenV1MintAndStoreNFTParallel
| EntryPoints::TokenV1MintAndStoreNFTSequential
@@ -365,6 +387,9 @@ impl EntryPoints {
EntryPoints::CreateObjects { .. } | EntryPoints::CreateObjectsConflict { .. } => {
"objects"
},
+ EntryPoints::VectorTrimAppend { .. }
+ | EntryPoints::VectorRemoveInsert { .. }
+ | EntryPoints::VectorRangeMove { .. } => "vector_example",
EntryPoints::TokenV1InitializeCollection
| EntryPoints::TokenV1MintAndStoreNFTParallel
| EntryPoints::TokenV1MintAndStoreNFTSequential
@@ -556,6 +581,51 @@ impl EntryPoints {
bcs::to_bytes(other.expect("Must provide other")).unwrap(),
],
),
+ EntryPoints::VectorTrimAppend {
+ vec_len,
+ element_len,
+ index,
+ repeats,
+ }
+ | EntryPoints::VectorRemoveInsert {
+ vec_len,
+ element_len,
+ index,
+ repeats,
+ } => get_payload(
+ module_id,
+ ident_str!(
+ if let EntryPoints::VectorTrimAppend { .. } = self {
+ "test_trim_append"
+ } else {
+ "test_remove_insert"
+ }
+ )
+ .to_owned(),
+ vec![
+ bcs::to_bytes(vec_len).unwrap(),
+ bcs::to_bytes(element_len).unwrap(),
+ bcs::to_bytes(index).unwrap(),
+ bcs::to_bytes(repeats).unwrap(),
+ ],
+ ),
+ EntryPoints::VectorRangeMove {
+ vec_len,
+ element_len,
+ index,
+ move_len,
+ repeats,
+ } => get_payload(
+ module_id,
+ ident_str!("test_middle_range_move").to_owned(),
+ vec![
+ bcs::to_bytes(vec_len).unwrap(),
+ bcs::to_bytes(element_len).unwrap(),
+ bcs::to_bytes(index).unwrap(),
+ bcs::to_bytes(move_len).unwrap(),
+ bcs::to_bytes(repeats).unwrap(),
+ ],
+ ),
EntryPoints::TokenV1InitializeCollection => get_payload_void(
module_id,
ident_str!("token_v1_initialize_collection").to_owned(),
@@ -825,6 +895,9 @@ impl EntryPoints {
EntryPoints::CreateObjects { .. } | EntryPoints::CreateObjectsConflict { .. } => {
AutomaticArgs::Signer
},
+ EntryPoints::VectorTrimAppend { .. }
+ | EntryPoints::VectorRemoveInsert { .. }
+ | EntryPoints::VectorRangeMove { .. } => AutomaticArgs::None,
EntryPoints::TokenV1InitializeCollection
| EntryPoints::TokenV1MintAndStoreNFTParallel
| EntryPoints::TokenV1MintAndStoreNFTSequential
diff --git a/crates/transaction-generator-lib/src/publishing/raw_module_data.rs b/crates/transaction-generator-lib/src/publishing/raw_module_data.rs
index 195b894efff28..8ea4ea55f9a03 100644
--- a/crates/transaction-generator-lib/src/publishing/raw_module_data.rs
+++ b/crates/transaction-generator-lib/src/publishing/raw_module_data.rs
@@ -1000,11 +1000,11 @@ pub static MODULES_SIMPLE: Lazy>> = Lazy::new(|| { vec![
pub static PACKAGE_FRAMEWORK_USECASES_METADATA: Lazy> = Lazy::new(|| {
vec![
17, 70, 114, 97, 109, 101, 119, 111, 114, 107, 85, 115, 101, 99, 97, 115, 101, 115,
- 1, 0, 0, 0, 0, 0, 0, 0, 0, 64, 55, 49, 50, 55, 50, 50, 66, 65,
- 65, 50, 67, 49, 53, 66, 70, 54, 65, 70, 53, 70, 55, 52, 65, 67, 69, 56,
- 51, 68, 53, 66, 66, 54, 66, 67, 56, 56, 49, 69, 66, 67, 68, 66, 66, 57,
- 52, 48, 49, 54, 66, 66, 53, 66, 67, 48, 49, 51, 56, 49, 49, 50, 51, 53,
- 66, 50, 215, 1, 31, 139, 8, 0, 0, 0, 0, 0, 2, 255, 165, 144, 187, 142,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 64, 67, 51, 69, 57, 51, 69, 69, 54,
+ 70, 68, 66, 54, 67, 65, 65, 55, 52, 70, 50, 48, 50, 55, 70, 56, 68, 54,
+ 65, 51, 55, 70, 69, 70, 68, 50, 65, 69, 69, 54, 56, 70, 56, 65, 57, 57,
+ 70, 54, 55, 70, 65, 48, 57, 52, 65, 48, 68, 50, 67, 56, 57, 70, 54, 56,
+ 48, 55, 215, 1, 31, 139, 8, 0, 0, 0, 0, 0, 2, 255, 165, 144, 187, 142,
194, 64, 12, 69, 251, 249, 10, 107, 182, 38, 236, 15, 108, 193, 238, 138, 150, 6,
170, 8, 33, 51, 49, 33, 100, 176, 163, 241, 240, 144, 16, 255, 78, 44, 30, 130,
22, 100, 23, 215, 246, 189, 167, 112, 217, 97, 104, 177, 166, 185, 99, 220, 18, 252,
@@ -1016,13 +1016,14 @@ pub static PACKAGE_FRAMEWORK_USECASES_METADATA: Lazy> = Lazy::new(|| {
134, 107, 160, 99, 88, 35, 215, 38, 101, 5, 65, 88, 51, 114, 6, 172, 170, 68,
170, 96, 20, 5, 236, 5, 197, 88, 184, 242, 182, 183, 231, 117, 187, 101, 108, 116,
77, 105, 113, 55, 219, 163, 143, 163, 223, 191, 127, 239, 46, 112, 10, 188, 112, 161,
- 1, 0, 0, 6, 18, 97, 103, 103, 114, 101, 103, 97, 116, 111, 114, 95, 101, 120,
+ 1, 0, 0, 7, 18, 97, 103, 103, 114, 101, 103, 97, 116, 111, 114, 95, 101, 120,
97, 109, 112, 108, 101, 0, 0, 0, 12, 99, 111, 105, 110, 95, 101, 120, 97, 109,
112, 108, 101, 0, 0, 0, 22, 102, 117, 110, 103, 105, 98, 108, 101, 95, 97, 115,
115, 101, 116, 95, 101, 120, 97, 109, 112, 108, 101, 0, 0, 0, 7, 111, 98, 106,
101, 99, 116, 115, 0, 0, 0, 23, 114, 101, 115, 111, 117, 114, 99, 101, 95, 103,
114, 111, 117, 112, 115, 95, 101, 120, 97, 109, 112, 108, 101, 0, 0, 0, 8, 116,
- 111, 107, 101, 110, 95, 118, 49, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0,
+ 111, 107, 101, 110, 95, 118, 49, 0, 0, 0, 14, 118, 101, 99, 116, 111, 114, 95,
+ 101, 120, 97, 109, 112, 108, 101, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 1, 14, 65, 112, 116, 111, 115, 70, 114, 97, 109, 101,
119, 111, 114, 107, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@@ -1602,6 +1603,52 @@ pub static MODULE_FRAMEWORK_USECASES_TOKEN_V1: Lazy> = Lazy::new(|| {
]
});
+#[rustfmt::skip]
+pub static MODULE_FRAMEWORK_USECASES_VECTOR_EXAMPLE: Lazy> = Lazy::new(|| {
+ vec![
+ 161, 28, 235, 11, 7, 0, 0, 10, 8, 1, 0, 4, 3, 4, 46, 4, 50, 8,
+ 5, 58, 81, 7, 139, 1, 97, 8, 236, 1, 64, 16, 172, 2, 31, 12, 203, 2,
+ 213, 2, 0, 0, 1, 3, 0, 1, 0, 1, 0, 1, 0, 2, 5, 6, 0, 1,
+ 1, 4, 7, 8, 1, 0, 1, 1, 5, 9, 6, 1, 0, 1, 0, 6, 5, 6,
+ 0, 1, 1, 7, 7, 11, 1, 0, 1, 1, 8, 12, 6, 1, 0, 1, 2, 3,
+ 3, 3, 5, 3, 6, 3, 2, 3, 3, 1, 10, 10, 3, 1, 3, 1, 10, 3,
+ 11, 10, 3, 3, 1, 3, 10, 10, 3, 1, 3, 3, 10, 3, 7, 3, 10, 10,
+ 3, 4, 3, 3, 3, 3, 0, 2, 7, 10, 9, 0, 3, 1, 9, 0, 3, 7,
+ 10, 9, 0, 3, 9, 0, 3, 10, 10, 3, 1, 10, 3, 1, 10, 9, 0, 2,
+ 7, 10, 9, 0, 10, 9, 0, 3, 10, 10, 3, 1, 10, 10, 3, 14, 118, 101,
+ 99, 116, 111, 114, 95, 101, 120, 97, 109, 112, 108, 101, 12, 103, 101, 110, 101, 114,
+ 97, 116, 101, 95, 118, 101, 99, 18, 116, 101, 115, 116, 95, 114, 101, 109, 111, 118,
+ 101, 95, 105, 110, 115, 101, 114, 116, 6, 118, 101, 99, 116, 111, 114, 6, 114, 101,
+ 109, 111, 118, 101, 6, 105, 110, 115, 101, 114, 116, 16, 116, 101, 115, 116, 95, 116,
+ 114, 105, 109, 95, 97, 112, 112, 101, 110, 100, 4, 116, 114, 105, 109, 6, 97, 112,
+ 112, 101, 110, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 171, 205,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 20, 99, 111, 109,
+ 112, 105, 108, 97, 116, 105, 111, 110, 95, 109, 101, 116, 97, 100, 97, 116, 97, 9,
+ 0, 3, 50, 46, 48, 3, 50, 46, 49, 0, 0, 0, 0, 4, 65, 64, 2, 0,
+ 0, 0, 0, 0, 0, 0, 0, 12, 2, 6, 0, 0, 0, 0, 0, 0, 0, 0,
+ 12, 3, 9, 12, 4, 5, 8, 5, 23, 10, 4, 4, 62, 11, 3, 6, 1, 0,
+ 0, 0, 0, 0, 0, 0, 22, 12, 3, 10, 3, 10, 1, 35, 3, 19, 5, 23,
+ 13, 2, 10, 3, 68, 2, 5, 6, 64, 3, 0, 0, 0, 0, 0, 0, 0, 0,
+ 12, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 12, 5, 9, 12, 7, 11, 0,
+ 12, 8, 5, 33, 5, 57, 10, 7, 4, 59, 11, 5, 6, 1, 0, 0, 0, 0,
+ 0, 0, 0, 22, 12, 5, 10, 5, 10, 8, 35, 3, 44, 5, 57, 10, 2, 12,
+ 10, 13, 10, 6, 0, 0, 0, 0, 0, 0, 0, 0, 67, 2, 12, 11, 10, 5,
+ 11, 11, 21, 13, 6, 11, 10, 68, 3, 5, 31, 11, 6, 2, 8, 12, 7, 5,
+ 39, 8, 12, 4, 5, 14, 1, 1, 4, 0, 10, 34, 11, 0, 11, 1, 17, 0,
+ 12, 4, 6, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 9, 12, 5, 5, 10,
+ 5, 30, 10, 5, 4, 31, 11, 0, 6, 1, 0, 0, 0, 0, 0, 0, 0, 22,
+ 12, 0, 10, 0, 10, 3, 35, 3, 21, 5, 30, 13, 4, 10, 2, 56, 0, 12,
+ 6, 13, 4, 10, 2, 11, 6, 56, 1, 5, 8, 2, 8, 12, 5, 5, 16, 4,
+ 1, 4, 0, 13, 33, 11, 0, 11, 1, 17, 0, 12, 4, 6, 0, 0, 0, 0,
+ 0, 0, 0, 0, 12, 0, 9, 12, 5, 5, 10, 5, 29, 10, 5, 4, 30, 11,
+ 0, 6, 1, 0, 0, 0, 0, 0, 0, 0, 22, 12, 0, 10, 0, 10, 3, 35,
+ 3, 21, 5, 29, 13, 4, 10, 2, 56, 2, 12, 6, 13, 4, 11, 6, 56, 3,
+ 5, 8, 2, 8, 12, 5, 5, 16, 0,
+ ]
+});
+
#[rustfmt::skip]
pub static MODULES_FRAMEWORK_USECASES: Lazy>> = Lazy::new(|| { vec![
MODULE_FRAMEWORK_USECASES_AGGREGATOR_EXAMPLE.to_vec(),
@@ -1610,6 +1657,7 @@ pub static MODULES_FRAMEWORK_USECASES: Lazy>> = Lazy::new(|| { vec![
MODULE_FRAMEWORK_USECASES_OBJECTS.to_vec(),
MODULE_FRAMEWORK_USECASES_RESOURCE_GROUPS_EXAMPLE.to_vec(),
MODULE_FRAMEWORK_USECASES_TOKEN_V1.to_vec(),
+ MODULE_FRAMEWORK_USECASES_VECTOR_EXAMPLE.to_vec(),
]});
#[rustfmt::skip]
pub static PACKAGE_AMBASSADOR_TOKEN_METADATA: Lazy> = Lazy::new(|| {
diff --git a/testsuite/module-publish/src/packages/framework_usecases/sources/vector_example.move b/testsuite/module-publish/src/packages/framework_usecases/sources/vector_example.move
new file mode 100644
index 0000000000000..6dbff776a0d8f
--- /dev/null
+++ b/testsuite/module-publish/src/packages/framework_usecases/sources/vector_example.move
@@ -0,0 +1,47 @@
+
+/// test speed of vector operations
+module 0xABCD::vector_example {
+ use std::vector;
+
+ fun generate_vec(vec_len: u64, element_len: u64): vector> {
+ let elem = vector::empty();
+ for (i in 0..element_len) {
+ vector::push_back(&mut elem, i);
+ };
+ let vec = vector::empty();
+ for (i in 0..vec_len) {
+ let cur = elem;
+ cur[0] = i;
+ vector::push_back(&mut vec, cur);
+ };
+ vec
+ }
+
+ public entry fun test_trim_append(vec_len: u64, element_len: u64, index: u64, repeats: u64) {
+ let vec = generate_vec(vec_len, element_len);
+
+ for (i in 0..repeats) {
+ let part = vector::trim(&mut vec, index);
+ vector::append(&mut vec, part);
+ };
+ }
+
+ public entry fun test_remove_insert(vec_len: u64, element_len: u64, index: u64, repeats: u64) {
+ let vec = generate_vec(vec_len, element_len);
+
+ for (i in 0..repeats) {
+ let part = vector::remove(&mut vec, index);
+ vector::insert(&mut vec, index, part);
+ };
+ }
+
+ // public entry fun test_middle_range_move(vec_len: u64, element_len: u64, index: u64, move_len: u64, repeats: u64) {
+ // let vec1 = generate_vec(vec_len, element_len);
+ // let vec2 = generate_vec(vec_len, element_len);
+
+ // for (i in 0..repeats) {
+ // vector::move_range(&mut vec1, index, move_len, &mut vec2, index);
+ // vector::move_range(&mut vec2, index, move_len, &mut vec1, index);
+ // };
+ // }
+}
diff --git a/testsuite/single_node_performance.py b/testsuite/single_node_performance.py
index 69a571cbadbc6..119ba8a250efd 100755
--- a/testsuite/single_node_performance.py
+++ b/testsuite/single_node_performance.py
@@ -253,6 +253,9 @@ class RunGroupConfig:
RunGroupConfig(key=RunGroupKey("no-op-fee-payer", module_working_set_size=DEFAULT_MODULE_WORKING_SET_SIZE), included_in=Flow.CONTINUOUS),
RunGroupConfig(key=RunGroupKey("simple-script"), included_in=LAND_BLOCKING_AND_C),
+ RunGroupConfig(key=RunGroupKey("vector-trim-append-len3000-size1"), included_in=Flow.CONTINUOUS, waived=True),
+ RunGroupConfig(key=RunGroupKey("vector-remove-insert-len3000-size1"), included_in=Flow.CONTINUOUS, waived=True),
+
RunGroupConfig(expected_tps=50000, key=RunGroupKey("coin_transfer_connected_components", executor_type="sharded"), key_extra=RunGroupKeyExtra(sharding_traffic_flags="--connected-tx-grps 5000", transaction_type_override=""), included_in=Flow.REPRESENTATIVE, waived=True),
RunGroupConfig(expected_tps=50000, key=RunGroupKey("coin_transfer_hotspot", executor_type="sharded"), key_extra=RunGroupKeyExtra(sharding_traffic_flags="--hotspot-probability 0.8", transaction_type_override=""), included_in=Flow.REPRESENTATIVE, waived=True),
diff --git a/testsuite/single_node_performance_values.tsv b/testsuite/single_node_performance_values.tsv
index 49145e221deac..279fe00d20f45 100644
--- a/testsuite/single_node_performance_values.tsv
+++ b/testsuite/single_node_performance_values.tsv
@@ -42,6 +42,8 @@ deserialize-u256 1 VM 60 0.869 1.021 37002.8
no-op-fee-payer 1 VM 60 0.952 1.026 2058.3
no-op-fee-payer 100 VM 60 0.828 1.016 32284.2
simple-script 1 VM 60 0.830 1.024 37707.7
+vector-trim-append-len3000-size1 1 VM 60 0.830 1.024 535
+vector-remove-insert-len3000-size1 1 VM 60 0.830 1.024 514
no_commit_apt-fa-transfer 1 VM 60 0.927 1.015 29357.5
no_commit_apt-fa-transfer 1 NativeVM 60 0.922 1.021 44946.3
no_commit_apt-fa-transfer 1 AptosVMSpeculative 60 0.953 1.010 1851.9