Tye*_*Rik 5 arrays ethereum solidity truffle remix
我正在尝试通过优化代码来节省汽油。然而,我突然想知道在 Solidity 中从一个数组复制到另一个数组的最佳实践是什么。
我提出两个选择。一种是通过指针复制(我猜),另一种是使用 for 循环。
TestOne.sol
contract TestContract {
uint32[4] testArray;
constructor(uint32[4] memory seeds) {
testArray = seeds; // execution costs: 152253
}
function Show() public returns (uint32[4] memory) {
return testArray;
}
}
Run Code Online (Sandbox Code Playgroud)
测试二.sol
contract TestContract {
uint32[4] testArray;
constructor(uint32[4] memory seeds) {
for(uint i = 0; i < 4; i++) {
testArray[i] = seeds[i]; // execution costs: 150792
}
}
function Show() public returns (uint32[4] memory) {
return testArray;
}
}
Run Code Online (Sandbox Code Playgroud)
我使用 Remix(以太坊在线 IDE)、0.8.13 Solidity 编译器和启用优化进行了测试(200)
我们可以看到,TestOne 使用了152253 Gas作为执行成本,TestTwo 使用了150792 Gas作为执行成本。
有趣的是,for 循环使用的 Gas 比仅仅分配指针要少。在我看来,for 循环会比其他循环更多的汇编代码。(至少会有赋值 、uint i
替换 4 次、检查条件 4 次(是否i < 4
)、增加i++
4 次等。)
我怀疑solidity编译器的“优化”。但是,在没有“启用优化”的情况下进行相同的小实验后,它会得到与 for 循环使用更少的气体相同的结果。(198846 与 198464)
为什么会出现以上的情况呢?
从数组复制到数组的最佳实践是什么?有没有类似C++的复制功能std::copy()
?
小智 4
最佳实践是将数组从内存复制到存储,而不循环遍历其项目。然而,这个例子中的合约优化很棘手。官方文档是这样说的:
如果您希望初始合约部署更便宜,而后续函数执行更昂贵,请将其设置为
--optimize-runs=1
。如果您预计会有很多事务并且不关心较高的部署成本和输出大小,请设置--optimize-runs
为较高的数字。
为了说明上述情况,请考虑以下合同:
// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
contract TestLoop {
uint32[4] testArray;
function setArrayWithLoop(uint32[4] memory array) public {
for(uint256 i = 0; i < array.length; i++)
testArray[i] = array[i];
}
function setArrayWithoutLoop(uint32[4] memory array) public {
testArray = array;
}
function show() public view returns (uint32[4] memory) {
return testArray;
}
}
contract NoLoop {
uint32[4] testArray;
constructor(uint32[4] memory array) {
testArray = array;
}
function show() public view returns (uint32[4] memory) {
return testArray;
}
}
contract Loop {
uint32[4] testArray;
constructor (uint32[4] memory array) {
for(uint256 i = 0; i < array.length; i++)
testArray[i] = array[i];
}
function show() public view returns (uint32[4] memory) {
return testArray;
}
}
Run Code Online (Sandbox Code Playgroud)
和使用以下方式编写的脚本brownie
:
// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
contract TestLoop {
uint32[4] testArray;
function setArrayWithLoop(uint32[4] memory array) public {
for(uint256 i = 0; i < array.length; i++)
testArray[i] = array[i];
}
function setArrayWithoutLoop(uint32[4] memory array) public {
testArray = array;
}
function show() public view returns (uint32[4] memory) {
return testArray;
}
}
contract NoLoop {
uint32[4] testArray;
constructor(uint32[4] memory array) {
testArray = array;
}
function show() public view returns (uint32[4] memory) {
return testArray;
}
}
contract Loop {
uint32[4] testArray;
constructor (uint32[4] memory array) {
for(uint256 i = 0; i < array.length; i++)
testArray[i] = array[i];
}
function show() public view returns (uint32[4] memory) {
return testArray;
}
}
Run Code Online (Sandbox Code Playgroud)
与以下brownie-config.yaml
:
from brownie import TestLoop, NoLoop, Loop, accounts
def function_calls():
contract = TestLoop.deploy({'from': accounts[0]})
print('set array in loop')
contract.setArrayWithLoop([1, 2, 3, 4], {'from': accounts[1]})
print('array ', contract.show(), '\n\n')
print('set array by copy from memory to storage')
contract.setArrayWithoutLoop([10, 9, 8, 7], {'from': accounts[2]})
print('array ', contract.show(), '\n\n')
def deploy_no_loop():
print('deploy NoLoop contract')
contract = NoLoop.deploy([21, 22, 23, 24], {'from': accounts[3]})
print('array ', contract.show(), '\n\n')
def deploy_loop():
print('deploy Loop contract')
contract = Loop.deploy([31, 32, 33, 34], {'from': accounts[3]})
print('array ', contract.show(), '\n\n')
def main():
function_calls()
deploy_no_loop()
deploy_loop()
Run Code Online (Sandbox Code Playgroud)
给出以下输出:
compiler:
solc:
version: 0.8.13
optimizer:
enabled: true
runs: 1
Run Code Online (Sandbox Code Playgroud)
gas used
从功能setArrayWithoutLoop
和功能上比较setArrayWithLoop
。