{"content":{"title":"Gas 优化 - 幂运算与连乘的 gas 消耗","body":"[solidity 文档](https:\/\/learnblockchain.cn\/docs\/solidity\/types.html#id11)中提到，如果幂运算的指数较小时，使用连乘会更节约 gas ，那这个临界值在哪呢，幂运算的指数到多少之后会比连乘节约 gas 呢？能节约多少呢，毕竟蚊子腿也是肉\r\n\r\n> 在“checked” 模式下，幂运算仅会为小基数使用相对便宜的 `exp` 操作码。 例如 `x**3` 的例子，表达式 `x*x*x` 也许更便宜。 在任何情况下，都建议进行 gas 消耗测试和使用优化器。\r\n\r\n## 计算方法\r\n\r\nGas 消耗计算方法参考：[计算Solidity 函数的Gas 消耗](https:\/\/learnblockchain.cn\/article\/2716)。使用 Hardhat 进行测试。\r\n\r\n### 1. 测试合约代码\r\n\r\n```solidity\r\n\/\/ SPDX-License-Identifier: MIT\r\npragma solidity ^0.8.0;\r\nimport \"@openzeppelin\/contracts\/utils\/Strings.sol\";\r\n\r\ninterface IGasCostTest{\r\n    function setX() external;\r\n    function setY() external;\r\n}\r\n\r\ncontract GasCostTest{\r\n    uint256 public x = 2;\r\n    uint256 public y = 2;\r\n\r\n    function setX() external {\r\n        x = x ** 9;\r\n    }\r\n\r\n    function setY() external {\r\n        y = y * y * y * y * y * y * y * y * y;\r\n    }\r\n}\r\n\r\ncontract CalculateGasCost {\r\n    string public report;\r\n    IGasCostTest public TestAddress;\r\n    \r\n    constructor(address testAddress_){\r\n        TestAddress = IGasCostTest(testAddress_);\r\n    }\r\n\r\n    function GasCost(\r\n        string memory name,\r\n        function() external fun\r\n    ) public {\r\n        uint256 u0 = gasleft();\r\n        fun();\r\n        uint256 u1 = gasleft();\r\n        uint256 diff = u0 - u1;\r\n        string memory s = report;\r\n        report = string(abi.encodePacked(s, string(abi.encodePacked(name, \" GasCost: \", Strings.toString(diff))), \"\\n\"));\r\n    }\r\n\r\n    function GenReport() public {\r\n        GasCost(\"setX \", TestAddress.setX);\r\n        GasCost(\"setY \", TestAddress.setY);\r\n    }\r\n}\r\n```\r\n\r\n### 2. Hardhat 测试代码\r\n\r\n```js\r\nconst { expect } = require(\"chai\");\r\nconst { ethers } = require(\"hardhat\");\r\n\r\ndescribe(\"Test x ^ 9\", function () {\r\n    let ContractAddress, CalculateGasCostAddress;\r\n\r\n    before('布署合约', async function () {\r\n        const ContractInstance = await ethers.getContractFactory(\"GasCostTest\");\r\n        const CalculateGasCostInstance = await ethers.getContractFactory(\"CalculateGasCost\");\r\n        ContractAddress = await ContractInstance.deploy();\r\n        CalculateGasCostAddress = await CalculateGasCostInstance.deploy(ContractAddress.address);\r\n    });\r\n\r\n    it(\"CalculateGasCost\", async function () {\r\n        await CalculateGasCostAddress.GenReport();\r\n        await CalculateGasCostAddress.GenReport();\r\n        const res = await CalculateGasCostAddress.report();\r\n        console.log(res);\r\n        const x = await ContractAddress.x();\r\n        const y = await ContractAddress.y();\r\n        expect(x).to.equal(y);\r\n    });\r\n});\r\n```\r\n\r\n### 3. 结果\r\n\r\n- 未使用 remix 优化器：\r\n\r\n```\r\nTest x ^ 3\r\nsetX  GasCost: 8446\r\nsetY  GasCost: 6202\r\nsetX  GasCost: 8492\r\nsetY  GasCost: 6202\r\n\r\nTest x ^ 9\r\nsetX  GasCost: 8446\r\nsetY  GasCost: 8314\r\nsetX  GasCost: 9073\r\nsetY  GasCost: 8314\r\n\r\nTest x ^ 10\r\nsetX  GasCost: 8446\r\nsetY  GasCost: 8666\r\nsetX  GasCost: 9073\r\nsetY  GasCost: 8666\r\n```\r\n\r\n- 使用 remix 优化器\r\n\r\n```\r\nTest x ^ 3\r\nsetX GasCost: 8242 \r\nsetY GasCost: 5674 \r\nsetX GasCost: 8345 \r\nsetY GasCost: 5674\r\n\r\n# 因为会导致 uint256 溢出，所以只执行一次\r\nTest x ^ 30\r\nsetX GasCost: 8242 \r\nsetY GasCost: 8239\r\n\r\nTest x ^ 31\r\nsetX GasCost: 8242 \r\nsetY GasCost: 8334\r\n```\r\n\r\n### 4. 位运算\r\n\r\n```\r\n# 未使用 remix 优化器：\r\nTest 1 << 3\r\nsetX  GasCost: 8468\r\nsetZ  GasCost: 5520\r\n\r\nTest 1 << 250\r\nsetX  GasCost: 8468\r\nsetZ  GasCost: 5529\r\n\r\n# 使用 remix 优化器：\r\nTest 1 << 3\r\nsetX GasCost: 8264 \r\nsetZ GasCost: 5500\r\n\r\nTest 1 << 250\r\nsetX GasCost: 8264 \r\nsetZ GasCost: 5506\r\n```\r\n\r\n## 结论\r\n\r\n1. 在未使用 remix 优化器下，幂运算的指数小于 9 时使用连乘能节约一定的 gas；使用 remix 优化器时，幂运算的指数小于 31 时使用连乘能节约一定的 gas；\r\n2. 底数是 2 时，使用位运算会是个更好的选择；\r\n3. 连乘数量较多时，代码可读性会变差，使用时需斟酌。"},"author":{"user":"https:\/\/learnblockchain.cn\/people\/7502","address":"0x0ecD7439B7B449181ac5C19dB623d9C46e4C7A67"},"history":null,"timestamp":1666081405,"version":1}