Skip to content
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

[move-stdlib] Add vector::move_range native function #14863

Open
wants to merge 4 commits into
base: igor/move_mem_swap
Choose a base branch
from

Conversation

igor-aptos
Copy link
Contributor

Description

Memcopy (i.e. ptr::copy_nonoverlapping inside of Vec) is extremely efficient, and using Vec operations that use it directly is significantly faster (orders of magnitude on bigger vectors) than issuing operations in move.

Operations on vector that can be speed-up: insert, remove, append, split_off.

To keep amount of native functions short, instead of having native for each of those, providing one more general native function: vector::move_range, which is enough to support all 4 of the above, in addition to other uses.

Internally, we shortcircuit a few special cases, for faster speed.

How Has This Been Tested?

Full performance evaluation is in the follow-up PR: #14862

Key Areas to Review

Type of Change

  • Performance improvement

Which Components or Systems Does This Change Impact?

  • Move/Aptos Virtual Machine

Checklist

  • I have read and followed the CONTRIBUTING doc
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I identified and added all stakeholders and component owners affected by this change as reviewers
  • I tested both happy and unhappy path of the functionality
  • I have made corresponding changes to the documentation

Copy link

trunk-io bot commented Oct 3, 2024

⏱️ 2h 43m total CI duration on this PR
Slowest 15 Jobs Cumulative Duration Recent Runs
rust-move-unit-coverage 16m 🟩
rust-move-unit-coverage 16m 🟩
rust-move-unit-coverage 16m 🟩
rust-move-unit-coverage 14m 🟩
rust-move-unit-coverage 12m 🟩
rust-move-unit-coverage 11m 🟩
rust-move-tests 10m 🟩
rust-move-tests 10m 🟩
rust-move-unit-coverage 10m
rust-move-tests 10m 🟥
rust-move-tests 10m 🟩
rust-move-tests 9m 🟩
rust-cargo-deny 9m 🟩🟩🟩🟩🟩
check-dynamic-deps 4m 🟩🟩🟩🟩🟩
general-lints 2m 🟩🟩🟩🟩🟩

settingsfeedbackdocs ⋅ learn more about trunk.io

Copy link

codecov bot commented Oct 3, 2024

Codecov Report

Attention: Patch coverage is 75.65217% with 28 lines in your changes missing coverage. Please review.

Project coverage is 60.1%. Comparing base (8b0d35b) to head (b36187b).

Files with missing lines Patch % Lines
...party/move/move-vm/types/src/values/values_impl.rs 61.7% 18 Missing ⚠️
...s-move/framework/move-stdlib/src/natives/vector.rs 84.3% 8 Missing ⚠️
...party/move/move-vm/runtime/src/native_functions.rs 88.2% 2 Missing ⚠️
Additional details and impacted files
@@                 Coverage Diff                  @@
##           igor/move_mem_swap   #14863    +/-   ##
====================================================
  Coverage                60.1%    60.1%            
====================================================
  Files                     857      858     +1     
  Lines                  211341   211455   +114     
====================================================
+ Hits                   127147   127233    +86     
- Misses                  84194    84222    +28     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@igor-aptos igor-aptos force-pushed the igor/native_vector_move_range branch 2 times, most recently from d724063 to e4540db Compare October 8, 2024 19:00
@igor-aptos igor-aptos changed the base branch from igor/vector_utilities to igor/move_mem_swap October 8, 2024 19:01
Comment on lines 2486 to 2489
// potentially unnecessary as native call should've checked the types already
// (unlike other vector functions that are bytecodes)
check_elem_layout(type_param, from_c)?;
check_elem_layout(type_param, to_c)?;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

one question - given no checks in mem::swap, should I have these checks here?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good question. It has the same indirect guarantees as the from_self != to_self.

Comment on lines 97 to 101
if module_name.as_str() == "string"
&& vector_bytecode_instruction_methods.contains(func_name.as_str())
{
println!("ERROR: Tried to register as native a vector bytecode_instruction method {}, skipping.", func_name.as_str());
continue;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure if this check is even needed (this is mostly to make sure we don't create new native function with same name as bytecode instruction), or if it's better to remove it - or on the other hand panic here.

but this is basically to be a fixed replacement of the filter in aptos-move/aptos-vm/src/natives.rs

@@ -2307,6 +2307,25 @@ fn check_elem_layout(ty: &Type, v: &Container) -> PartialVMResult<()> {
}

impl VectorRef {
pub fn len_usize_raw(&self, type_param: &Type) -> PartialVMResult<usize> {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added this to not need to wrap / unwrap / cast for getting the length inside of native function.

Comment on lines 64 to 69
/// Moves range of elements `[removal_position, removal_position + length)` from vector `from`,
/// to vector `to`, inserting them starting at the `insert_position`.
/// In the `from` vector, elements after the selected range are moved left to fill the hole
/// (i.e. range is removed, while the order of the rest of the elements is kept)
/// In the `to` vector, elements after the `insert_position` are moved the the right to make space for new elements
/// (i.e. range is inserted, while the order of the rest of the elements is kept)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add that to the comment here.

/// (i.e. range is removed, while the order of the rest of the elements is kept)
/// In the `to` vector, elements after the `insert_position` are moved the the right to make space for new elements
/// (i.e. range is inserted, while the order of the rest of the elements is kept)
native public fun range_move<T>(from: &mut vector<T>, removal_position: u64, length: u64, to: &mut vector<T>, insert_position: u64);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't move_range more intuitive and better descripting name?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

now that you say it, makes more sense :D

// Copyright (c) The Move Contributors
// SPDX-License-Identifier: Apache-2.0

//! Implementation of native functions for utf8 strings.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrong leftover comment


use super::mem::get_feature_not_available_error;

/// The generic type supplied to aggregator snapshots is not supported.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably some leftover comment from wherever a snipped was copied (aggregator_natives/aggregator_v2.rs?)

use super::mem::get_feature_not_available_error;

/// The generic type supplied to aggregator snapshots is not supported.
pub const EINDEX_OUT_OF_BOUNDS: u64 = 0x03_0001;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How are these constants picked?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there is this utility now - https://github.com/aptos-labs/aptos-core/blob/main/types/src/error.rs

I'll use that, but it might be worth revisiting where to put it, to not require importing whole aptos-types for that.

let removal_position = usize::try_from(safely_pop_arg!(args, u64)).map_err(map_err)?;
let from = safely_pop_arg!(args, VectorRef);

// need to fetch sizes here to charge upfront, and later in move_range, not sure if possible to combine
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't fully understand this comment without reading the code

@@ -2307,6 +2307,25 @@ fn check_elem_layout(ty: &Type, v: &Container) -> PartialVMResult<()> {
}

impl VectorRef {
pub fn len_usize_raw(&self, type_param: &Type) -> PartialVMResult<usize> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

length_as_usize? Why raw?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure , as usize sounds good.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

raw was meant to mean not as a move value - i.e. not Value::usize (which doesn't exist), but just rust usize

};
Ok(len)
}

pub fn len(&self, type_param: &Type) -> PartialVMResult<Value> {
let c: &Container = self.0.container();
check_elem_layout(type_param, c)?;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use the new function here:
Ok(Value::u64(length_as_usize(....))

@@ -2452,6 +2471,67 @@ impl VectorRef {
self.0.mark_dirty();
Ok(())
}

pub fn move_range(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd make it static (associated):
this: &Self instead of &self and use it with VectorRef::move_range(....

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also need to either document, or check that self != to_self

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VM will guarantee there cannot be two &mut references to same self.

I can still defensively check, but it should never be possible to go there.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The pre-condition for this function is self != to_self (we can just document it, as a comment, and mention that it's enforced by the overall VM design).

@@ -81,8 +81,26 @@ impl NativeFunctions {
where
I: IntoIterator<Item = (AccountAddress, Identifier, Identifier, NativeFunction)>,
{
let vector_bytecode_instruction_methods = HashSet::from([
"empty",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason for adding this check now (in this PR)?

Do we really want to prevent this? There's nothing wrong with having native functions that have the same functionality as instructions.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

previous comment:

not sure if this check is even needed (this is mostly to make sure we don't create new native function with same name as bytecode instruction), or if it's better to remove it - or on the other hand panic here.

but this is basically to be a fixed replacement of the filter in aptos-move/aptos-vm/src/natives.rs

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think there's a general reason to disallow these in MoveVM. I'd rather keep the filter in AptosVM. Is there any other reason for wanting to remove it from AptosVM?

@igor-aptos
Copy link
Contributor Author

updated PR with addressed comments

Copy link
Contributor

@ziaptos ziaptos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More like a question that I have before stamping this.

Comment on lines 2486 to 2489
// potentially unnecessary as native call should've checked the types already
// (unlike other vector functions that are bytecodes)
check_elem_layout(type_param, from_c)?;
check_elem_layout(type_param, to_c)?;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good question. It has the same indirect guarantees as the from_self != to_self.

@@ -81,8 +81,26 @@ impl NativeFunctions {
where
I: IntoIterator<Item = (AccountAddress, Identifier, Identifier, NativeFunction)>,
{
let vector_bytecode_instruction_methods = HashSet::from([
"empty",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think there's a general reason to disallow these in MoveVM. I'd rather keep the filter in AptosVM. Is there any other reason for wanting to remove it from AptosVM?

@igor-aptos
Copy link
Contributor Author

addressed all comments. move the filtering back to AptosVM. added comment for redundant check

{
return Err(SafeNativeError::Abort {
abort_code: EINDEX_OUT_OF_BOUNDS,
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There appears to be a typo in the comment: moved the the right contains a duplicated word and should be moved to the right

Spotted by Graphite Reviewer

Is this helpful? React 👍 or 👎 to let us know.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants