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

Gas computation: use uint64 instead of uint256 #35

Closed
mratsim opened this issue May 22, 2018 · 2 comments
Closed

Gas computation: use uint64 instead of uint256 #35

mratsim opened this issue May 22, 2018 · 2 comments

Comments

@mratsim
Copy link
Contributor

mratsim commented May 22, 2018

Using uint64 instead of uint256 for gas computation like in Go-Ethereum would be much better for devices from a memory and performance standpoint.

See Go-Ethereum PR - ethereum/go-ethereum#3674.

Furthermore, gas prices can then be stored in a const array and replaced at instantiation site at compile-time instead of being loaded into memory.

This can be tested with

const a = [1111, 2222, 3333, 4444]
echo a[2]

and checking the generated C code.

@mratsim
Copy link
Contributor Author

mratsim commented May 23, 2018

Note that block gas limit and so transaction gas limits hovers arount 8M currently so a int64 can be better to avoid uint surprises when substracting. (Yellow paper, 4.3.4)

There is still a question about dealing with int exceptions (on debug only) but if there are gas computation issues it might be safer to crash anyway.

@mratsim
Copy link
Contributor Author

mratsim commented May 24, 2018

For the moment I will go ahead with this type.

  GasInt* = int64
    ## Type alias used for gas computation

For reference:

  • Go-Ethereum uses uint64 with explicit overflow checks.
    // NOTE: The following methods need to be optimised using either bit checking or asm
    
    // SafeSub returns subtraction result and whether overflow occurred.
    func SafeSub(x, y uint64) (uint64, bool) {
      return x - y, x < y
    }
    
    // SafeAdd returns the result and whether overflow occurred.
    func SafeAdd(x, y uint64) (uint64, bool) {
      return x + y, y > MaxUint64-x
    }
    
    // SafeMul returns multiplication result and whether overflow occurred.
    func SafeMul(x, y uint64) (uint64, bool) {
      if x == 0 || y == 0 {
        return 0, false
      }
      return x * y, y > MaxUint64/x
    }
  • Parity uses u256 and tries to drop down to u64 as often as possible. Note that contrary to Geth, Parity allows overflowing (to avoid exceptions/errors?).
    /// Cost calculation type. For low-gas usage we calculate costs using usize instead of U256
    pub trait CostType: Sized + From<usize> + Copy
      + ops::Mul<Output=Self> + ops::Div<Output=Self> + ops::Add<Output=Self> +ops::Sub<Output=Self>
      + ops::Shr<usize, Output=Self> + ops::Shl<usize, Output=Self>
      + cmp::Ord + fmt::Debug {
      /// Converts this cost into `U256`
      fn as_u256(&self) -> U256;
      /// Tries to fit `U256` into this `Cost` type
      fn from_u256(val: U256) -> Result<Self>;
      /// Convert to usize (may panic)
      fn as_usize(&self) -> usize;
      /// Add with overflow
      fn overflow_add(self, other: Self) -> (Self, bool);
      /// Multiple with overflow
      fn overflow_mul(self, other: Self) -> (Self, bool);
      /// Single-step full multiplication and shift: `(self*other) >> shr`
      /// Should not overflow on intermediate steps
      fn overflow_mul_shr(self, other: Self, shr: usize) -> (Self, bool);
    }
  • Today (May-2018) each block is capped at about 8 millions gas, i.e. about 2^22 bits needed
    while int64 can represent up to 2^63 - 1 for the positive range.
  • Edit: Py-EVM explicitly limit gas to 2^63-1. https://github.com/status-im/nimbus/blob/502941a388b6110adaab9697f9a375e0dc598eda/src/constants.nim#L191
  • Compared to Go and Rust, Nim defaults to signed ints, and overflow checks can be turned on in release mode.
    Furthermore, they are implemented with portable GCC/LLVM builtins for C/C++ backends, with assembly fallback and pure Nim fallback.

There is also an open question on whether to use range types like range[0'i64 .. high(int64)] instead of just int64?

On the plus side:

  • negative gas balance should be an exception anyway
  • there are no negative gas cost besides refunding gas logic for the SSTORE opcode when clearing memory (can be special-cased).

On the minus side:

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

No branches or pull requests

1 participant