About gas fee optimization

About gas fee optimization

First, let's take a look at this code

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract GasGolf{

    uint public total;
    //[1,2,3,4,5,100]
    function sum(uint[] memory nums) external{
        for(uint i = 0;i<nums.length;i+=1){
            bool isEven = nums[i] % 2 == 0;
            bool isLessThan99 = nums[i] < 99;
            if(isEven && isLessThan99){
                total += nums[i];
            }
        }
    }
}

In this code, a total state variable is defined, and it is passed into the even number of the array and the number less than 99 to accumulate.

When we first run the sum method, we can see that the gas cost for the first execution is

28654

Now we start the first optimization: change memory to calldata

contract GasGolf{
    uint public total;
    //[1,2,3,4,5,100]
    function sum(uint[] calldata nums) external{
        for(uint i = 0;i<nums.length;i+=1){
            bool isEven = nums[i] % 2 == 0;
            bool isLessThan99 = nums[i] < 99;
            if(isEven && isLessThan99){
                total += nums[i];
            }
        }
    }
}

At this point, let's look at the gas fee that needs to be used. At this time, it is about 2000 less.

26909

Why is this so?

Let's compare the difference between calldata and memory

In Solidity, calldatait refers to the storage location of the parameters of the function call. Rather, it ismemory the storage location of temporary variables declared inside the function.

The reason why the usage calldataratio memorysaves gas costs is that calldatait is read-only and cannot be modified during function execution. On the contrary, memorythe data in can be modified during the execution of the function, which means that if you use a memorylarge data structure to store, each modification needs to consume more gas costs.

In this scenario, we can see that nums does not seem to need to be changed as an input parameter, so here we can directly use calldata.

Let's look inside the loop body again

We found that the state variable total will be reassigned in each cycle. This kind of operation at the level of the overall state variable is actually a waste of gas.

We might as well change the way of thinking, extract the total out of the loop, and set a variable in the method to copy the total to the memory, that is, we accumulate a variable in the memory every time, so it will not be written State variables. At the end of the recycle, the result is written to the state variable at one time.

code show as below:

contract GasGolf{
    uint public total;
    //[1,2,3,4,5,100]
    function sum(uint[] calldata nums) external{
        uint _total = total;
        for(uint i = 0;i<nums.length;i+=1){
            bool isEven = nums[i] % 2 == 0;
            bool isLessThan99 = nums[i] < 99;
            if(isEven && isLessThan99){
                _total += nums[i];
            }
        }
        total = _total;
    }
}

At this time, we can execute to see the consumption of gas cost, and save about 200 at this time

26698

Let's take a look at what else can be optimized.

It is not difficult to see that there is actually no need to assign values ​​to isEven and isLessThan99 separately every time. We can directly merge them into the conditional judgment. The combined effect is as follows:

contract GasGolf{
    uint public total;
    //[1,2,3,4,5,100]
    function sum(uint[] calldata nums) external{
        uint _total = total;
        for(uint i = 0;i<nums.length;i+=1){
            if(nums[i] % 2 == 0 && nums[i] < 99){
                _total += nums[i];
            }
        }
        total = _total;
    }
}

At this time, the gas cost is saved by about 300

26380

Let's look at the code for(uint i = 0;i<nums.length;i+=1), i+=1 will actually copy the value of i to a temporary variable, then increment i, and finally Then return the value of the temporary variable to the expression. Is there a way to avoid creating temporary variables? The answer is yes: we just need to change to ++i

contract GasGolf{
    uint public total;
    //[1,2,3,4,5,100]
    function sum(uint[] calldata nums) external{
        uint _total = total;
        for(uint i = 0;i<nums.length;++i){
            if(nums[i] % 2 == 0 && nums[i] < 99){
                _total += nums[i];
            }
        }
        total = _total;
    }
}

At this time, the gas cost has been saved by more than 300

26008

In the same loop just now, we can see that the length of the array must be read in each loop, so that this method must be executed every loop, so since its length is constant, we might as well directly store it in cache variable. code show as below:

contract GasGolf{
    uint public total;
    //[1,2,3,4,5,100]
    function sum(uint[] calldata nums) external{
        uint _total = total;
        uint len = nums.length;
        for(uint i = 0;i<len;++i){
            if(nums[i] % 2 == 0 && nums[i] < 99){
                _total += nums[i];
            }
        }
        total = _total;
    }
}

At this time, the gas fee has been saved by more than 100

25973

Looking at the code inside the loop, we can actually copy the element nums[i] of the array in the loop body to the memory in advance, the code is as follows:

contract GasGolf{
    uint public total;
    //[1,2,3,4,5,100]
    function sum(uint[] calldata nums) external{
        uint _total = total;
        uint len = nums.length;
        for(uint i = 0;i<len;++i){
            uint num = nums[i];
            if(num % 2 == 0 && num < 99){
                _total += num;
            }
        }
        total = _total;
    }
}

At this time, the gas cost is saved by about 200

25811

Guess you like

Origin blog.csdn.net/weixin_43918614/article/details/130320811