如何测试百万级数组Solidity合约二分查找的Gas消耗?
测试100万元素数组二分查找的Gas消耗:Foundry/Forge 方案
Foundry 完全能解决你的需求,而且相比 Hardhat 速度提升非常明显——它基于本地 EVM 执行测试,没有 JS 层的通信开销,百万级数据的填充和测试效率远超 Hardhat。以下是具体实现方案:
核心思路
- 编写带有序数组存储和二分查找的目标合约
- 用 Foundry 的 Solidity 测试脚本,要么模拟真实交易分步存入数据,要么直接操作合约存储快速填充数组
- 在测试中直接计算二分查找的 Gas 消耗
实现步骤
1. 目标合约示例
先写一个包含数组存储和二分查找的合约(假设元素是按顺序存入的,符合二分查找的前置条件):
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; contract SortedArray { uint256[] public elements; // 模拟外部交易存入元素 function addElement(uint256 element) external { elements.push(element); } // 二分查找实现 function binarySearch(uint256 target) external view returns (bool found, uint256 index) { uint256 left = 0; uint256 right = elements.length - 1; while (left <= right) { uint256 mid = left + (right - left) / 2; uint256 midElement = elements[mid]; if (midElement == target) { return (true, mid); } else if (midElement < target) { left = mid + 1; } else { right = mid - 1; } } return (false, type(uint256).max); } }
2. Foundry 测试脚本
方案一:模拟真实交易分步存入
完全还原「不同交易逐步存入」的场景,用 vm.prank 模拟不同地址发起交易:
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "forge-std/Test.sol"; import "../src/SortedArray.sol"; contract SortedArrayTest is Test { SortedArray public sortedArray; function setUp() public { sortedArray = new SortedArray(); } function testBinarySearchGas() public { uint256 totalElements = 1_000_000; // 模拟100万次不同地址的存入交易 for (uint256 i = 0; i < totalElements; i++) { vm.prank(address(uint160(i))); sortedArray.addElement(i); } // 测试查找中间元素(二分查找的典型场景) uint256 target = 500_000; uint256 gasBefore = gasleft(); (bool found, uint256 index) = sortedArray.binarySearch(target); uint256 gasUsed = gasBefore - gasleft(); assertTrue(found); assertEq(index, target); console.log("Binary search gas used for 1M elements:", gasUsed); } }
方案二:快速填充数组(跳过交易模拟)
如果只关注二分查找的 Gas 消耗,不需要模拟存入交易的开销,可以直接操作合约存储快速填充:
function testBinarySearchGasFast() public { uint256 totalElements = 1_000_000; // 直接修改合约存储,设置数组长度 bytes32 arrayLengthSlot = keccak256(abi.encode(uint256(0))); vm.store(address(sortedArray), arrayLengthSlot, bytes32(totalElements)); // 批量写入数组元素 for (uint256 i = 0; i < totalElements; i++) { bytes32 elementSlot = keccak256(abi.encode(uint256(0))) + i; vm.store(address(sortedArray), elementSlot, bytes32(i)); } // 测试查找 uint256 target = 500_000; uint256 gasBefore = gasleft(); (bool found, uint256 index) = sortedArray.binarySearch(target); uint256 gasUsed = gasBefore - gasleft(); assertTrue(found); assertEq(index, target); console.log("Binary search gas used (fast fill):", gasUsed); }
3. 运行测试
执行以下命令运行测试,加上 -vv 可以看到 Gas 日志:
forge test --match-test testBinarySearchGas -vv
如果需要更详细的 Gas 报告,使用:
forge test --match-test testBinarySearchGas --gas-report
关键优势
- 速度:Foundry 的 Solidity 测试执行速度是 Hardhat JS 测试的几十倍,百万级循环几分钟就能完成
- 灵活:既可以模拟真实交易场景,也能快速填充数据聚焦核心测试目标
- 精准:直接在 Solidity 测试中用
gasleft()计算 Gas 消耗,结果准确可靠
内容的提问来源于stack exchange,提问作者David Galoyan
相关产品推荐
相关产品推荐

