kandi background
kandi background
Explore Kits
kandi background
Explore Kits
kandi background
Explore Kits
kandi background
Explore Kits
Explore all Ethereum open source software, libraries, packages, source code, cloud functions and APIs.

Popular New Releases in Ethereum

Osun (v1.10.16)

0.61.0 Release

Version 0.8.13

v1.7.3

go-ethereum

Osun (v1.10.16)

nushell

0.61.0 Release

openzeppelin-contracts

solidity

Version 0.8.13

web3.js

v1.7.3

Popular Libraries in Ethereum

Trending New libraries in Ethereum

Top Authors in Ethereum

1

96 Libraries

98814

2

76 Libraries

19716

3

51 Libraries

3253

4

46 Libraries

2899

5

42 Libraries

886

6

40 Libraries

2043

7

40 Libraries

1456

8

36 Libraries

11243

9

34 Libraries

22629

10

31 Libraries

1980

1

96 Libraries

98814

2

76 Libraries

19716

3

51 Libraries

3253

4

46 Libraries

2899

5

42 Libraries

886

6

40 Libraries

2043

7

40 Libraries

1456

8

36 Libraries

11243

9

34 Libraries

22629

10

31 Libraries

1980

Trending Kits in Ethereum

No Trending Kits are available at this moment for Ethereum

Trending Discussions on Ethereum

    What is the best practice of copying from array to array in Solidity?
    How to locally unit-test Chainlink's Verifiable Random Function?
    Trying to run "brownie run .\scripts\deploy.py --network rinkeby" but getting a ValueError
    Flutter - How to refresh a Widget on button Click?
    Flag provided but not defined: -rpc
    Brownie testing for reverted transactions does not work with pytest.raises() or brownie.reverts()
    Solidity version to learn
    ParserError: Source file requires different compiler version
    How to delete an image saved in IPFS blockchain?
    Is it possible to call a ERC20 function inside a ERC721 contract?

QUESTION

What is the best practice of copying from array to array in Solidity?

Asked 2022-Apr-10 at 21:23

I am trying to save gas by optimize code. In a flash, however, I was wondered what is the best practice of copying from array to array in Solidity.

I present two option. One is copying by pointer (I guess) and the other is using for-loop.

TestOne.sol

1contract TestContract {
2    uint32[4] testArray;
3
4    constructor(uint32[4] memory seeds) {
5        testArray = seeds; // execution costs: 152253
6    }
7
8    function Show() public returns (uint32[4] memory) {
9        return testArray;
10    }
11}
12

TestTwo.sol

1contract TestContract {
2    uint32[4] testArray;
3
4    constructor(uint32[4] memory seeds) {
5        testArray = seeds; // execution costs: 152253
6    }
7
8    function Show() public returns (uint32[4] memory) {
9        return testArray;
10    }
11}
12contract TestContract {
13    uint32[4] testArray;
14
15    constructor(uint32[4] memory seeds) {
16        for(uint i = 0; i < 4; i++) {
17            testArray[i] = seeds[i];  // execution costs: 150792
18        }
19    }
20
21    function Show() public returns (uint32[4] memory) {
22        return testArray;
23    }
24}
25

I tested with Remix (Ethereum Online IDE), 0.8.13 Solidity Compiler with Enable optimization (200)

Discussion of test result

We can see that, TestOne used 152253 gas for execution costs, and TestTwo used 150792 gas for execution costs.

The funny thing is that, for-loop used less gas than just assigning pointer. In my little thought, for-loop would be more assembly codes than the other. (There would be, at least, assigning uint i, substitute 4 times, check conditions 4 times (whether i < 4), increase i++ 4 times etc.)

I suspected the "optimization" of solidity compiler. But, after doing same small experiment without "Enable optimization", it does same result that for-loop used less gas. (198846 vs. 198464)

The Question is

  1. Why do above things happened?

  2. What is the best practice of copying from array to array? Is there any copy function like C++'s std::copy() ?

ANSWER

Answered 2022-Apr-10 at 21:23

The best practice is copy array from memory to storage without looping over their items. However contract optimization in this example is tricky. The official documentation says as follow:

If you want the initial contract deployment to be cheaper and the later function executions to be more expensive, set it to --optimize-runs=1. If you expect many transactions and do not care for higher deployment cost and output size, set --optimize-runs to a high number.

To illustrate above, consider following contracts:

copy icondownload icon

1contract TestContract {
2    uint32[4] testArray;
3
4    constructor(uint32[4] memory seeds) {
5        testArray = seeds; // execution costs: 152253
6    }
7
8    function Show() public returns (uint32[4] memory) {
9        return testArray;
10    }
11}
12contract TestContract {
13    uint32[4] testArray;
14
15    constructor(uint32[4] memory seeds) {
16        for(uint i = 0; i &lt; 4; i++) {
17            testArray[i] = seeds[i];  // execution costs: 150792
18        }
19    }
20
21    function Show() public returns (uint32[4] memory) {
22        return testArray;
23    }
24}
25// SPDX-License-Identifier: MIT
26pragma solidity 0.8.13;
27
28contract TestLoop {
29    uint32[4] testArray;
30
31    function setArrayWithLoop(uint32[4] memory array) public {
32        for(uint256 i = 0; i &lt; array.length; i++)
33            testArray[i] = array[i];
34    }
35
36    function setArrayWithoutLoop(uint32[4] memory array) public {
37        testArray = array;
38    }
39
40    function show() public view returns (uint32[4] memory) {
41        return testArray;
42    }
43}
44
45contract NoLoop {
46    uint32[4] testArray;
47
48    constructor(uint32[4] memory array) {
49        testArray = array;
50    }
51
52    function show() public view returns (uint32[4] memory) {
53        return testArray;
54    }
55}
56
57contract Loop {
58    uint32[4] testArray;
59
60    constructor (uint32[4] memory array) {
61        for(uint256 i = 0; i &lt; array.length; i++)
62            testArray[i] = array[i];
63    }
64
65    function show() public view returns (uint32[4] memory) {
66        return testArray;
67    }
68}
69

and script written by using brownie:

copy icondownload icon

1contract TestContract {
2    uint32[4] testArray;
3
4    constructor(uint32[4] memory seeds) {
5        testArray = seeds; // execution costs: 152253
6    }
7
8    function Show() public returns (uint32[4] memory) {
9        return testArray;
10    }
11}
12contract TestContract {
13    uint32[4] testArray;
14
15    constructor(uint32[4] memory seeds) {
16        for(uint i = 0; i &lt; 4; i++) {
17            testArray[i] = seeds[i];  // execution costs: 150792
18        }
19    }
20
21    function Show() public returns (uint32[4] memory) {
22        return testArray;
23    }
24}
25// SPDX-License-Identifier: MIT
26pragma solidity 0.8.13;
27
28contract TestLoop {
29    uint32[4] testArray;
30
31    function setArrayWithLoop(uint32[4] memory array) public {
32        for(uint256 i = 0; i &lt; array.length; i++)
33            testArray[i] = array[i];
34    }
35
36    function setArrayWithoutLoop(uint32[4] memory array) public {
37        testArray = array;
38    }
39
40    function show() public view returns (uint32[4] memory) {
41        return testArray;
42    }
43}
44
45contract NoLoop {
46    uint32[4] testArray;
47
48    constructor(uint32[4] memory array) {
49        testArray = array;
50    }
51
52    function show() public view returns (uint32[4] memory) {
53        return testArray;
54    }
55}
56
57contract Loop {
58    uint32[4] testArray;
59
60    constructor (uint32[4] memory array) {
61        for(uint256 i = 0; i &lt; array.length; i++)
62            testArray[i] = array[i];
63    }
64
65    function show() public view returns (uint32[4] memory) {
66        return testArray;
67    }
68}
69from brownie import TestLoop, NoLoop, Loop, accounts
70
71def function_calls():
72    contract = TestLoop.deploy({'from': accounts[0]})
73    print('set array in loop')
74    contract.setArrayWithLoop([1, 2, 3, 4], {'from': accounts[1]})
75    print('array ', contract.show(), '\n\n')
76
77    print('set array by copy from memory to storage')
78    contract.setArrayWithoutLoop([10, 9, 8, 7], {'from': accounts[2]})
79    print('array ', contract.show(), '\n\n')
80
81def deploy_no_loop():
82    print('deploy NoLoop contract')
83    contract = NoLoop.deploy([21, 22, 23, 24], {'from': accounts[3]})
84    print('array ', contract.show(), '\n\n')
85
86def deploy_loop():
87    print('deploy Loop contract')
88    contract = Loop.deploy([31, 32, 33, 34], {'from': accounts[3]})
89    print('array ', contract.show(), '\n\n')
90
91def main():
92    function_calls()
93    deploy_no_loop()
94    deploy_loop()
95

with following brownie-config.yaml:

copy icondownload icon

1contract TestContract {
2    uint32[4] testArray;
3
4    constructor(uint32[4] memory seeds) {
5        testArray = seeds; // execution costs: 152253
6    }
7
8    function Show() public returns (uint32[4] memory) {
9        return testArray;
10    }
11}
12contract TestContract {
13    uint32[4] testArray;
14
15    constructor(uint32[4] memory seeds) {
16        for(uint i = 0; i &lt; 4; i++) {
17            testArray[i] = seeds[i];  // execution costs: 150792
18        }
19    }
20
21    function Show() public returns (uint32[4] memory) {
22        return testArray;
23    }
24}
25// SPDX-License-Identifier: MIT
26pragma solidity 0.8.13;
27
28contract TestLoop {
29    uint32[4] testArray;
30
31    function setArrayWithLoop(uint32[4] memory array) public {
32        for(uint256 i = 0; i &lt; array.length; i++)
33            testArray[i] = array[i];
34    }
35
36    function setArrayWithoutLoop(uint32[4] memory array) public {
37        testArray = array;
38    }
39
40    function show() public view returns (uint32[4] memory) {
41        return testArray;
42    }
43}
44
45contract NoLoop {
46    uint32[4] testArray;
47
48    constructor(uint32[4] memory array) {
49        testArray = array;
50    }
51
52    function show() public view returns (uint32[4] memory) {
53        return testArray;
54    }
55}
56
57contract Loop {
58    uint32[4] testArray;
59
60    constructor (uint32[4] memory array) {
61        for(uint256 i = 0; i &lt; array.length; i++)
62            testArray[i] = array[i];
63    }
64
65    function show() public view returns (uint32[4] memory) {
66        return testArray;
67    }
68}
69from brownie import TestLoop, NoLoop, Loop, accounts
70
71def function_calls():
72    contract = TestLoop.deploy({'from': accounts[0]})
73    print('set array in loop')
74    contract.setArrayWithLoop([1, 2, 3, 4], {'from': accounts[1]})
75    print('array ', contract.show(), '\n\n')
76
77    print('set array by copy from memory to storage')
78    contract.setArrayWithoutLoop([10, 9, 8, 7], {'from': accounts[2]})
79    print('array ', contract.show(), '\n\n')
80
81def deploy_no_loop():
82    print('deploy NoLoop contract')
83    contract = NoLoop.deploy([21, 22, 23, 24], {'from': accounts[3]})
84    print('array ', contract.show(), '\n\n')
85
86def deploy_loop():
87    print('deploy Loop contract')
88    contract = Loop.deploy([31, 32, 33, 34], {'from': accounts[3]})
89    print('array ', contract.show(), '\n\n')
90
91def main():
92    function_calls()
93    deploy_no_loop()
94    deploy_loop()
95compiler:
96  solc:
97    version: 0.8.13
98    optimizer:
99      enabled: true
100      runs: 1
101

which gives following outputs:

copy icondownload icon

1contract TestContract {
2    uint32[4] testArray;
3
4    constructor(uint32[4] memory seeds) {
5        testArray = seeds; // execution costs: 152253
6    }
7
8    function Show() public returns (uint32[4] memory) {
9        return testArray;
10    }
11}
12contract TestContract {
13    uint32[4] testArray;
14
15    constructor(uint32[4] memory seeds) {
16        for(uint i = 0; i &lt; 4; i++) {
17            testArray[i] = seeds[i];  // execution costs: 150792
18        }
19    }
20
21    function Show() public returns (uint32[4] memory) {
22        return testArray;
23    }
24}
25// SPDX-License-Identifier: MIT
26pragma solidity 0.8.13;
27
28contract TestLoop {
29    uint32[4] testArray;
30
31    function setArrayWithLoop(uint32[4] memory array) public {
32        for(uint256 i = 0; i &lt; array.length; i++)
33            testArray[i] = array[i];
34    }
35
36    function setArrayWithoutLoop(uint32[4] memory array) public {
37        testArray = array;
38    }
39
40    function show() public view returns (uint32[4] memory) {
41        return testArray;
42    }
43}
44
45contract NoLoop {
46    uint32[4] testArray;
47
48    constructor(uint32[4] memory array) {
49        testArray = array;
50    }
51
52    function show() public view returns (uint32[4] memory) {
53        return testArray;
54    }
55}
56
57contract Loop {
58    uint32[4] testArray;
59
60    constructor (uint32[4] memory array) {
61        for(uint256 i = 0; i &lt; array.length; i++)
62            testArray[i] = array[i];
63    }
64
65    function show() public view returns (uint32[4] memory) {
66        return testArray;
67    }
68}
69from brownie import TestLoop, NoLoop, Loop, accounts
70
71def function_calls():
72    contract = TestLoop.deploy({'from': accounts[0]})
73    print('set array in loop')
74    contract.setArrayWithLoop([1, 2, 3, 4], {'from': accounts[1]})
75    print('array ', contract.show(), '\n\n')
76
77    print('set array by copy from memory to storage')
78    contract.setArrayWithoutLoop([10, 9, 8, 7], {'from': accounts[2]})
79    print('array ', contract.show(), '\n\n')
80
81def deploy_no_loop():
82    print('deploy NoLoop contract')
83    contract = NoLoop.deploy([21, 22, 23, 24], {'from': accounts[3]})
84    print('array ', contract.show(), '\n\n')
85
86def deploy_loop():
87    print('deploy Loop contract')
88    contract = Loop.deploy([31, 32, 33, 34], {'from': accounts[3]})
89    print('array ', contract.show(), '\n\n')
90
91def main():
92    function_calls()
93    deploy_no_loop()
94    deploy_loop()
95compiler:
96  solc:
97    version: 0.8.13
98    optimizer:
99      enabled: true
100      runs: 1
101Running 'scripts/test_loop.py::main'...
102Transaction sent: 0x8380ef4abff179f08ba9704826fc44961d212e5ee10952ed3904b5ec7828c928
103  Gas price: 0.0 gwei   Gas limit: 12000000   Nonce: 0
104  TestLoop.constructor confirmed   Block: 1   Gas used: 251810 (2.10%)
105  TestLoop deployed at: 0x3194cBDC3dbcd3E11a07892e7bA5c3394048Cc87
106
107set array in loop
108Transaction sent: 0xfe72d6c878a980a9eeefee1dccdd0fe8214ee4772ab68ff0ac2b72708b7ab946
109  Gas price: 0.0 gwei   Gas limit: 12000000   Nonce: 0
110  TestLoop.setArrayWithLoop confirmed   Block: 2   Gas used: 49454 (0.41%)
111
112array  (1, 2, 3, 4) 
113
114
115set array by copy from memory to storage
116Transaction sent: 0x0106d1a7e37b155993a6d32d5cc9dc67696a55acd1cf29d2ed9dba0770436b98
117  Gas price: 0.0 gwei   Gas limit: 12000000   Nonce: 0
118  TestLoop.setArrayWithoutLoop confirmed   Block: 3   Gas used: 41283 (0.34%)
119
120array  (10, 9, 8, 7) 
121
122
123deploy NoLoop contract
124Transaction sent: 0x55ddded68300bb8f11b3b43580c58fed3431a2823bf3f82f0081c7bfce66f34d
125  Gas price: 0.0 gwei   Gas limit: 12000000   Nonce: 0
126  NoLoop.constructor confirmed   Block: 4   Gas used: 160753 (1.34%)
127  NoLoop deployed at: 0x7CA3dB74F7b6cd8D6Db1D34dEc2eA3c89a3417ec
128
129array  (21, 22, 23, 24) 
130
131
132deploy Loop contract
133Transaction sent: 0x1aa64f2cd527983df84cfdca5cfd7a281ff904cca227629ec8b0b29db561c043
134  Gas price: 0.0 gwei   Gas limit: 12000000   Nonce: 1
135  Loop.constructor confirmed   Block: 5   Gas used: 153692 (1.28%)
136  Loop deployed at: 0x2fb0fE4F05B7C8576F60A5BEEE35c23632Dc0C27
137
138array  (31, 32, 33, 34)
139

Conclusions

  1. When we consider contract function call optimization, then usage of memory to storage copy, here is more info, is more gas efficient than copy by for loop. Compare gas used from function setArrayWithoutLoop and function setArrayWithLoop.
  2. When we consider contract deployment optimization it seems there is opposite situation to this in conclusion 1.
  3. Most important: Contract constructor is called only once in contract lifetime, just when the contract is deployed to chain. So the most often there is an function call optimization rather than contract deployment optimization. This leads to conclusion 1.

Source https://stackoverflow.com/questions/71819186