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

VIP: Make dynamic calldata arrays lazy #4223

Open
Philogy opened this issue Sep 2, 2024 · 1 comment
Open

VIP: Make dynamic calldata arrays lazy #4223

Philogy opened this issue Sep 2, 2024 · 1 comment

Comments

@Philogy
Copy link

Philogy commented Sep 2, 2024

Simple Summary

Avoid redundant copies and memory allocations when interacting with dynamic arrays in calldata (Bytes[N], String[N] or DynArray[T, N]) by not copying them into memory by default.

Motivation

This is important for gas efficiency as it can significantly increase the gas cost when using calldata arrays, making it infeasible to use Vyper for use-cases where you need to process variable length arrays while accommodating potentially large lengths.

At Sorella e.g. we're building a Uniswap hook that aims to be able to interact with pools initiating swaps as well as settling large batches of user intents. The wasted cost of having the max size always be allocated and the lack of ability to lazily process elements one-by-one is a huge deterrent when considering Vyper as a smart contract language for our use-case.

Specification

This may not require any semantic or syntactical changes at all as the difference between calldata & memory could be abstracted away from the user, only ever copying the entirety of the object in memory if it's passed an argument to an internal function.

Alternatively to be more flexible such as use in internal functions storage location annotations could be added e.g.:

struct Order:
    amount: uint256
    price: uint256

def process_orders(orders: Calldata[DynArray[Order, 10_000]]):
    ...

Semantically, dynamic types with location Calldata[T] would behave identically from the smart contract dev's perspective, however at the codegen level the Calldata[T] type would inform the compiler that member or length access or slicing generate different code, only loading sections that are directly accessed at any time rather than loading it in its entirety by default.

This could also serve as a predecessor to a larger "data location" system in Vyper, similar but more expansive than Solidity's, allowing the compiler to understand and optimize interactions with different data locations (calldata, memory, returndata, self code, external code, transient storage, persistent storage).

Backwards Compatibility

Should not cause any backwards compatibility issues. The Calldata[T] annotation would be opt-in, if left out the compiler will interpret it as an in-memory array, copying calldata contents if the entry point of an external call.

Dependencies

(Unknown)

References

Somewhat inspired / related to recent criticisms of Vyper by others explaining how it doesn't handle dynamic data very well:
https://x.com/jtriley_eth/status/1830325738144448804.

Copyright

Copyright and related rights waived via CC0

@Philogy Philogy changed the title Make dynamic calldata arrays lazy (VIP) Make dynamic calldata arrays lazy Sep 2, 2024
@xrchz
Copy link
Contributor

xrchz commented Sep 2, 2024

This is a huge issue indeed. The data location system is sorely needed for Vyper - currently the excessive copying when calling internal functions, for example, is extremely painful.

@charles-cooper charles-cooper changed the title (VIP) Make dynamic calldata arrays lazy VIP: Make dynamic calldata arrays lazy Sep 5, 2024
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

No branches or pull requests

2 participants