gas优化: calldata与memory

1.作为外部输入参数

有以下两个合约:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
contract A {
    function set(uint256[] calldata arr) external {}
}

contract B {
    function set(uint256[] memory arr) external {}
}

这两个合约中,A传入的数组是calldata存储类型,而B是memory存储类型。我们发布之后,依次执行A、B合约中的set方法,传入的参数为数组[1,2,3,4],gas消耗如下:

存储类型 gas消耗
calldata 25710
memory 27281

可以发现calldata的gas消耗要比memory要小。因为外部输入的参数到达calldata的步骤要比到达memory的步骤更简单一些,所以消耗gas也要小些。

2.作为函数内调用参数

(1)外部函数为calldata,内部函数为calldata或memory

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
contract A {
    function set(uint256[] calldata arr) external {
        _core(arr);
    }

    function _core(uint256[] calldata arr) private {}
}

contract B {
    function set(uint256[] calldata arr) external {
        _core(arr);
    }

    function _core(uint256[] memory arr) private {}
}

这个测试的结果为:

外部函数参数 内部函数参数 gas消耗
calldata calldata 25749
calldata memory 25970

这个结果也很好理解。当内部函数参数为memory时,会发生一次从calldata到memory的拷贝操作,会消耗额外的gas,而从calldata到calldata,就仅仅是引用,不需要消耗额外的gas。

(2)外部函数为memory,内部函数为calldata或memory

contract C {
    function set(uint256[] memory arr) external {
        _core(arr);
    }

    function _core(uint256[] memory arr) private {}
}

contract D {
    // TypeError: Invalid type for argument in function call. 
    // Invalid implicit conversion from uint256[] memory to uint256[] calldata requested.
    function set(uint256[] memory arr) external {
        _core(arr);
    }

    function _core(uint256[] calldata arr) private {}
}

D合约代码中,当外部函数参数为memory,内部函数参数为calldata的时候,会报错:memory类型的uint256[]隐式转换到calldata类型的uint256[]是不合理的。
gas消耗的测试结果为:

外部函数参数 内部函数参数 gas消耗
memory memory 27314
memory calldata invalid

内外部函数参数都为memory类型时,虽然不会发生额外的拷贝操作,但是由于外部输入参数时calldata节省gas的优势,整体的gas消耗量还是最大的。

(3)结果总结

合约 外部函数参数 内部函数参数 gas消耗
A calldata calldata 25749
B calldata memory 25970
C memory memory 27314
D memory calldata invalid
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容