Gas 部分快照
Forge 可以在测试函数内部的任意部分捕获 gas 快照。这对于精确测量你的逻辑消耗了多少 gas 非常有用,因为外部调用和内部 gas 使用都会被测量。
你可以在测试中使用 snapshotGas
作弊代码 来捕获 gas 使用,而不是运行像 forge snapshot
或 forge test --gas-report
这样的命令,如下所示:
snapshotGas
作弊码
签名
/// Start a snapshot capture of the current gas usage by name.
/// The group name is derived from the contract name.
function startSnapshotGas(string calldata name) external;
/// Start a snapshot capture of the current gas usage by name in a group.
function startSnapshotGas(string calldata group, string calldata name) external;
/// Stop the snapshot capture of the current gas by latest snapshot name, capturing the gas used since the start.
function stopSnapshotGas() external returns (uint256 gasUsed);
/// Stop the snapshot capture of the current gas usage by name, capturing the gas used since the start.
/// The group name is derived from the contract name.
function stopSnapshotGas(string calldata name) external returns (uint256 gasUsed);
/// Stop the snapshot capture of the current gas usage by name in a group, capturing the gas used since the start.
function stopSnapshotGas(string calldata group, string calldata name) external returns (uint256 gasUsed);
/// Snapshot capture an arbitrary numerical value by name.
/// The group name is derived from the contract name.
function snapshotValue(string calldata name, uint256 value) external;
/// Snapshot capture an arbitrary numerical value by name in a group.
function snapshotValue(string calldata group, string calldata name, uint256 value) external;
/// Snapshot capture the gas usage of the last call by name from the callee perspective.
function snapshotGasLastCall(string calldata name) external returns (uint256 gasUsed);
/// Snapshot capture the gas usage of the last call by name in a group from the callee perspective.
function snapshotGasLastCall(string calldata group, string calldata name) external returns (uint256 gasUsed);
描述
snapshotGas*
作弊码允许你在测试中捕获 gas 使用情况。这对于跟踪你的逻辑消耗了多少 gas 非常有用。你可以通过名称捕获最后一次调用的 gas 使用情况,通过名称捕获任意数值,或者通过名称开始和停止当前 gas 使用情况的快照捕获。
为了严格比较测试运行之间的 gas 使用情况,在运行测试之前将 FORGE_SNAPSHOT_CHECK
环境变量设置为 true
。这将比较你的测试的 gas 使用情况与最后一次快照,如果 gas 使用情况发生变化则会失败。默认情况下,快照目录将在每次测试运行之前被新创建并清空其内容,以确保没有过时的数据存在。
使用 snapshotGas*
作弊码时创建的 snapshots
目录应当被纳入版本控制。这允许你跟踪 gas 使用情况随时间的变化,并在代码审查期间比较 gas 使用情况。
在运行 forge clean
时,snapshots
目录将被删除。
示例
捕获调用外部合约的代码段的 gas 使用情况:
contract SnapshotGasTest is Test {
uint256 public slot0;
Flare public flare;
function setUp() public {
flare = new Flare();
}
function testSnapshotGas() public {
vm.startSnapshotGas("externalA");
flare.run(256);
uint256 gasUsed = vm.stopSnapshotGas();
}
}
捕获多个修改内部状态的代码段的 gas 使用情况:
contract SnapshotGasTest is Test {
uint256 public slot0;
/// Writes to `snapshots/SnapshotGasTest.json` group with name `internalA`, `internalB`, and `internalC`.
function testSnapshotGas() public {
vm.startSnapshotGas("internalA");
slot0 = 1;
vm.stopSnapshotGas();
vm.startSnapshotGas("internalB");
slot0 = 2;
vm.stopSnapshotGas();
vm.startSnapshotGas("internalC");
slot0 = 0;
vm.stopSnapshotGas();
}
}
捕获同时修改内部状态并调用外部合约的代码段的 gas 使用情况:
contract SnapshotGasTest is Test {
uint256 public slot0;
Flare public flare;
function setUp() public {
flare = new Flare();
}
/// Writes to `snapshots/SnapshotGasTest.json` group with name `combinedA`.
function testSnapshotGas() public {
vm.startSnapshotGas("combinedA");
flare.run(256);
slot0 = 1;
vm.stopSnapshotGas();
}
}
Capturing an arbitrary numerical value (such as the bytecode size of a contract):
```solidity
contract SnapshotGasTest is Test {
uint256 public slot0;
/// 写入 `snapshots/SnapshotGasTest.json` 组,名称为 `valueA`、`valueB` 和 `valueC`。
function testSnapshotValue() public {
uint256 a = 123;
uint256 b = 456;
uint256 c = 789;
vm.snapshotValue("valueA", a);
vm.snapshotValue("valueB", b);
vm.snapshotValue("valueC", c);
}
}
Capturing the gas usage of the last call from the callee perspective:
contract SnapshotGasTest is Test {
Flare public flare;
function setUp() public {
flare = new Flare();
}
/// 写入 `snapshots/SnapshotGasTest.json` 组,名称为 `lastCallA`。
function testSnapshotGasLastCall() public {
flare.run(1);
vm.snapshotGasLastCall("lastCallA");
}
}
对于上述每个示例,也可以使用作弊码的 group
变体将这些快照分组到一个自定义组中。
contract SnapshotGasTest is Test {
uint256 public slot0;
/// 写入 `snapshots/CustomGroup.json` 组,名称为 `internalA`、`internalB` 和 `internalC`。
function testSnapshotGas() public {
vm.startSnapshotGas("CustomGroup", "internalA");
slot0 = 1;
vm.stopSnapshotGas();
vm.startSnapshotGas("CustomGroup", "internalB");
slot0 = 2;
vm.stopSnapshotGas();
vm.startSnapshotGas("CustomGroup", "internalC");
slot0 = 0;
vm.stopSnapshotGas();
}
}