美文网首页gas优化
gas优化: uint类型与evm字长

gas优化: uint类型与evm字长

作者: 梁帆 | 来源:发表于2022-11-02 11:47 被阅读0次

    首先需要明确EVM的基础知识:

    EVM采用了32字节(256bit)的字长,最多可以容纳2014个字,字为最小的操作单位。

    在知道这点之后,有编程经验的人可能已经知道了,这跟内存对齐有关系。我们开始讲uint256、uint16、uint8类型在使用时的注意点。

    1.uint8类型可能比uint256类型更加耗费gas

    有以下两个合约:

    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.7;
    contract A {
        uint256 value = 100;
    }
    
    contract B {
        uint8 value = 100;
    }
    

    合约B的想法很好理解,即uint8类型的数值范围在0~255100在这个范围内,因此赋予uint8类型的value会更节省gas。让我么来部署试试:

    合约 gas消耗
    A 102626 gas
    B 103088 gas

    从表格中可以发现,B的gas消耗是要比A高的,这是因为:
    EVM的存储字长是256位,因此不仅A中的value要分配1个字的长度,B中的value也要分配1个字的长度。此外由于B中value是uint8类型只有8位,所以EVM需要做额外的缩减操作,这样一来,gas就不减反增了。

    2.其他uint类型节省gas的正确使用

    有以下两个合约:

    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.7;
    contract A {
        uint256 value1 = 100;
        uint256 value2 = 100;
    }
    
    contract B {
        uint128 value1 = 100;
        uint128 value2 = 100;
    }
    

    按照上面的分析我们知道,A中需要分配2个字的长度,B中只需要分配1个字的长度,而且还不需要做缩减,因为两个128位类型加起来刚好等于1个字长,所以应该是B更节省gas。测试结果如下:

    合约 gas消耗
    A 128140 gas
    B 105044 gas

    结果和我们推理的一致。这里又有个新问题了,合约B中是两个uint128类型刚好凑成1个字长,如果是更小些的uint类型,凑不齐1个字长,那会是一个什么gas消耗情况呢?
    于是我又做了2个uint64, 2个uint32,两个uint8的测试,得出的结果如下:

    uint类型 占用字长 gas消耗
    256 2 128140 gas
    128 1 105044 gas
    64 1 104455 gas
    32 1 104161 gas
    8 1 103940 gas

    可以发现随着uint类型长度的缩减,两个该uint类型的值虽然还是只占1个字长,但是gas在同步地减少。

    这跟我们想的似乎还是不太一样,拿两个uint128和两个uint8举例,两个uint128刚好凑满1个字,而两个uint8凑不满1个字,还得做缩减,不是应该更加消耗gas吗?

    为了探求原因,我们做了debug,以下是两个合约部署时的汇编指令。
    这是两个uint8类型的:

    000 PUSH1 80
    002 PUSH1 40
    004 MSTORE
    005 PUSH1 64
    007 PUSH1 00
    009 DUP1
    010 PUSH2 0100
    013 EXP
    014 DUP2
    015 SLOAD
    016 DUP2
    017 PUSH1 ff
    019 MUL
    020 NOT
    021 AND
    022 SWAP1
    023 DUP4
    024 PUSH1 ff
    026 AND
    027 MUL
    028 OR
    029 SWAP1
    030 SSTORE
    031 POP
    032 PUSH1 64
    034 PUSH1 00
    036 PUSH1 01
    038 PUSH2 0100
    041 EXP
    042 DUP2
    043 SLOAD
    044 DUP2
    045 PUSH1 ff
    047 MUL
    048 NOT
    049 AND
    050 SWAP1
    051 DUP4
    052 PUSH1 ff
    054 AND
    055 MUL
    056 OR
    057 SWAP1
    058 SSTORE
    059 POP
    060 CALLVALUE
    061 DUP1
    062 ISZERO
    063 PUSH1 46
    065 JUMPI
    066 PUSH1 00
    068 DUP1
    069 REVERT
    070 JUMPDEST
    071 POP
    072 PUSH1 3f
    074 DUP1
    075 PUSH1 54
    077 PUSH1 00
    079 CODECOPY
    080 PUSH1 00
    082 RETURN
    083 INVALID
    084 PUSH1 80
    086 PUSH1 40
    088 MSTORE
    089 PUSH1 00
    091 DUP1
    092 REVERT
    093 INVALID
    094 LOG2
    095 PUSH5 6970667358
    101 INVALID
    102 SLT
    103 SHA3
    104 SWAP9
    105 PUSH29 ef37878a28ee38eb1289815a739b4d553cdc47c5ef777c6441080f4aa7
    135 DUP4
    136 PUSH5 736f6c6343
    142 STOP
    143 ADDMOD
    144 SMOD
    145 STOP
    146 CALLER
    

    这是两个uint128类型的:

    000 PUSH1 80
    002 PUSH1 40
    004 MSTORE
    005 PUSH1 64
    007 PUSH1 00
    009 DUP1
    010 PUSH2 0100
    013 EXP
    014 DUP2
    015 SLOAD
    016 DUP2
    017 PUSH16 ffffffffffffffffffffffffffffffff
    034 MUL
    035 NOT
    036 AND
    037 SWAP1
    038 DUP4
    039 PUSH16 ffffffffffffffffffffffffffffffff
    056 AND
    057 MUL
    058 OR
    059 SWAP1
    060 SSTORE
    061 POP
    062 PUSH1 64
    064 PUSH1 00
    066 PUSH1 10
    068 PUSH2 0100
    071 EXP
    072 DUP2
    073 SLOAD
    074 DUP2
    075 PUSH16 ffffffffffffffffffffffffffffffff
    092 MUL
    093 NOT
    094 AND
    095 SWAP1
    096 DUP4
    097 PUSH16 ffffffffffffffffffffffffffffffff
    114 AND
    115 MUL
    116 OR
    117 SWAP1
    118 SSTORE
    119 POP
    120 CALLVALUE
    121 DUP1
    122 ISZERO
    123 PUSH1 82
    125 JUMPI
    126 PUSH1 00
    128 DUP1
    129 REVERT
    130 JUMPDEST
    131 POP
    132 PUSH1 3f
    134 DUP1
    135 PUSH1 90
    137 PUSH1 00
    139 CODECOPY
    140 PUSH1 00
    142 RETURN
    143 INVALID
    144 PUSH1 80
    146 PUSH1 40
    148 MSTORE
    149 PUSH1 00
    151 DUP1
    152 REVERT
    153 INVALID
    154 LOG2
    155 PUSH5 6970667358
    161 INVALID
    162 SLT
    163 SHA3
    164 MOD
    165 INVALID
    166 GASLIMIT
    167 INVALID
    168 SWAP10
    169 PUSH21 fd3bdb4da6ad6a18fbbcdd61c5ce445b1bec4d94ee
    191 SGT
    192 INVALID
    193 INVALID
    194 MULMOD
    195 NUMBER
    196 PUSH5 736f6c6343
    202 STOP
    203 ADDMOD
    204 SMOD
    205 STOP
    206 CALLER
    

    3.调节变量声明顺序节省gas

    有以下两个合约:

    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.7;
    contract A {
        uint8 x = 100;
        uint256 y = 100;
        uint8 z = 100;
    }
    
    contract B {
        uint8 x = 100;
        uint8 z = 100;
        uint256 y = 100;
    }
    

    合约A、B中,它们仅仅是变量顺序不同。根据我们之前介绍EVM字长的基础知识,可以获知A合约占用了3个字长,而B合约占用了2个字长,因此B合约肯定更加节省gas,实验结果如下:

    合约 gas消耗
    A 154581 gas
    B 129454 gas

    和我们预知的一样。

    相关文章

      网友评论

        本文标题:gas优化: uint类型与evm字长

        本文链接:https://www.haomeiwen.com/subject/czzstdtx.html