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

Costly keccak of parameter #14056

Closed
drortirosh opened this issue Mar 19, 2023 · 1 comment
Closed

Costly keccak of parameter #14056

drortirosh opened this issue Mar 19, 2023 · 1 comment
Projects

Comments

@drortirosh
Copy link

Consider the following Remix sample.

keccak of calldata parameter takes extra 150gas for no apparent reason. a simple assembly wrapper can be used to demonstrate getting the same result for smaller cost.
This is a "low-hanging fruit" optimization. There are other places like this.

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.9.0;
import "hardhat/console.sol";

//deploy this contract to run the 2 tests
contract Asd {
    constructor() {
        bytes memory d = "A";
        new Test().work2(d);
        new Test().work1(d);
    }
}

contract Test {

    function calldataKeccak(bytes calldata data) internal pure returns (bytes32 ret) {
        assembly {
            let mem := mload(0x40)
            let len := data.length
            calldatacopy(mem, data.offset, len)
            ret := keccak256(mem, len)
        }
    }

    function work1(bytes calldata input) external view {
        uint g= gasleft();
        bytes32 data = keccak256(input);
        uint g1 = gasleft();
        console.logBytes32(data);
        console.log('gas used', g-g1);
    }

    function work2(bytes calldata input) external view {
        uint g= gasleft();
        bytes32 data = calldataKeccak(input);
        uint g1 = gasleft();
        console.logBytes32(data);
        console.log('gas used', g-g1);
    }

}

@r0qs
Copy link
Member

r0qs commented Mar 24, 2023

Hi @drortirosh thanks for reporting this. Please, consider the snippets below extracted from your example, ignoring the console.log() part:

  • sol-keccak.sol
contract Test {
    function run(bytes calldata data) external pure returns (bytes32 ret) {
        ret = keccak256(data);
    }
}
  • yul-keccak.sol
contract Test {
    function run(bytes calldata data) external pure returns (bytes32 ret) {
        assembly {
            let mem := mload(0x40)
            let len := data.length
            calldatacopy(mem, data.offset, len)
            ret := keccak256(mem, len)
        }
    }
}
forge debug --use 0.8.19 --via-ir --optimize sol-keccak.sol --sig "run(bytes)" 0x41
# uses 468 gas
forge debug --use 0.8.19 --via-ir --optimize yul-keccak.sol --sig "run(bytes)" 0x41
# uses 410 gas

If you run each of them using --ir-optimized compiler flag (e.g. solc --ir-optimized sol-keccak.sol), you will notice that the extra gas usage shown above comes from the fact that the Solidity code is doing proper memory allocation for the array and the assembly snippet is not. It does not move the free memory pointer or allocate a size field for the array.

This is an already know issue and it is similar to the following ones:

Optimizing this falls under #13722 which is already in our roadmap. So, I will be closing this issue as duplicated. Please, feel free to reopen it if necessary.

@r0qs r0qs closed this as completed Mar 24, 2023
Solidity automation moved this from Triage to Done Mar 24, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
No open projects
Solidity
  
Done
Development

No branches or pull requests

2 participants