Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add gas benchmarks for BatchLockup #359

Merged
merged 4 commits into from
Jun 29, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 0 additions & 53 deletions .gas-snapshot

This file was deleted.

259 changes: 259 additions & 0 deletions benchmark/BatchLockup.Gas.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.8.22;

import { ud2x18 } from "@prb/math/src/UD2x18.sol";
import { LockupDynamic, LockupLinear, LockupTranched } from "@sablier/v2-core/src/types/DataTypes.sol";

import { BatchLockup } from "../src/types/DataTypes.sol";
import { BatchLockupBuilder } from "../test/utils/BatchLockupBuilder.sol";
import { Benchmark_Test } from "./Benchmark.t.sol";

/// @notice Tests used to benchmark BatchLockup.
/// @dev This contract creates a Markdown file with the gas usage of each function.
contract BatchLockup_Gas_Test is Benchmark_Test {
/*//////////////////////////////////////////////////////////////////////////
STATE VARIABLES
//////////////////////////////////////////////////////////////////////////*/

uint128 internal constant AMOUNT_PER_COUNT = 10e18;
uint8[5] internal batches = [5, 10, 20, 30, 50];
uint8[5] internal counts = [24, 24, 24, 24, 12];

/*//////////////////////////////////////////////////////////////////////////
TEST FUNCTION
//////////////////////////////////////////////////////////////////////////*/

function testGas_Implementations() external {
// Set the file path.
benchmarkResultsFile = string.concat(benchmarkResults, "SablierV2BatchLockup.md");

// Create the file if it doesn't exist, otherwise overwrite it.
vm.writeFile({
path: benchmarkResultsFile,
data: string.concat(
"# Benchmarks for BatchLockup\n\n",
"| Function | Lockup Type | Segments/Tranches | Batch Size | Gas Usage |\n",
"| --- | --- | --- | --- | --- |\n"
)
});

for (uint256 i; i < batches.length; ++i) {
// Gas benchmark the batch create functions for Lockup Linear.
gasCreateWithDurationsLL(batches[i]);
gasCreateWithTimestampsLL(batches[i]);

// Gas benchmark the batch create functions for Lockup Dynamic.
gasCreateWithDurationsLD({ batchsize: batches[i], segmentsCount: counts[i] });
gasCreateWithTimestampsLD({ batchsize: batches[i], segmentsCount: counts[i] });

// Gas benchmark the batch create functions for Lockup Tranched.
gasCreateWithDurationsLT({ batchsize: batches[i], tranchesCount: counts[i] });
gasCreateWithTimestampsLT({ batchsize: batches[i], tranchesCount: counts[i] });
}
}

/*//////////////////////////////////////////////////////////////////////////
GAS BENCHMARKS FOR BATCH FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/

function gasCreateWithDurationsLD(uint256 batchsize, uint256 segmentsCount) internal {
BatchLockup.CreateWithDurationsLD[] memory params = BatchLockupBuilder.fillBatch(
defaults.createWithDurationsLD(
dai, uint128(AMOUNT_PER_COUNT * segmentsCount), _segmentsWithDuration(segmentsCount)
),
batchsize
);

uint256 beforeGas = gasleft();
batchLockup.createWithDurationsLD(lockupDynamic, dai, params);
string memory gasUsed = vm.toString(beforeGas - gasleft());

contentToAppend = string.concat(
"| `createWithDurationsLD` | Lockup Dynamic |",
vm.toString(segmentsCount),
" |",
vm.toString(batchsize),
" | ",
gasUsed,
" |"
);

// Append the content to the file.
appendToFile(benchmarkResultsFile, contentToAppend);
}

function gasCreateWithTimestampsLD(uint256 batchsize, uint256 segmentsCount) internal {
BatchLockup.CreateWithTimestampsLD[] memory params = BatchLockupBuilder.fillBatch(
defaults.createWithTimestampsLD(dai, uint128(AMOUNT_PER_COUNT * segmentsCount), _segments(segmentsCount)),
batchsize
smol-ninja marked this conversation as resolved.
Show resolved Hide resolved
);

uint256 beforeGas = gasleft();
batchLockup.createWithTimestampsLD(lockupDynamic, dai, params);
string memory gasUsed = vm.toString(beforeGas - gasleft());

contentToAppend = string.concat(
"| `createWithTimestampsLD` | Lockup Dynamic |",
vm.toString(segmentsCount),
" |",
vm.toString(batchsize),
" | ",
gasUsed,
" |"
);

// Append the data to the file
appendToFile(benchmarkResultsFile, contentToAppend);
}

function gasCreateWithDurationsLL(uint256 batchsize) internal {
BatchLockup.CreateWithDurationsLL[] memory params =
BatchLockupBuilder.fillBatch(defaults.createWithDurationsLL(dai), batchsize);

uint256 beforeGas = gasleft();
batchLockup.createWithDurationsLL(lockupLinear, dai, params);
string memory gasUsed = vm.toString(beforeGas - gasleft());

contentToAppend = string.concat(
"| `createWithDurationsLL` | Lockup Linear | |", vm.toString(batchsize), " | ", gasUsed, " |"
);

// Append the content to the file.
appendToFile(benchmarkResultsFile, contentToAppend);
}

function gasCreateWithTimestampsLL(uint256 batchsize) internal {
BatchLockup.CreateWithTimestampsLL[] memory params =
BatchLockupBuilder.fillBatch(defaults.createWithTimestampsLL(dai), batchsize);

uint256 beforeGas = gasleft();
batchLockup.createWithTimestampsLL(lockupLinear, dai, params);
string memory gasUsed = vm.toString(beforeGas - gasleft());

contentToAppend = string.concat(
"| `createWithTimestampsLL` | Lockup Linear | |", vm.toString(batchsize), " | ", gasUsed, " |"
);

// Append the data to the file
appendToFile(benchmarkResultsFile, contentToAppend);
}

function gasCreateWithDurationsLT(uint256 batchsize, uint256 tranchesCount) internal {
BatchLockup.CreateWithDurationsLT[] memory params = BatchLockupBuilder.fillBatch(
defaults.createWithDurationsLT(
dai, uint128(AMOUNT_PER_COUNT * tranchesCount), _tranchesWithDuration(tranchesCount)
),
batchsize
);

uint256 beforeGas = gasleft();
batchLockup.createWithDurationsLT(lockupTranched, dai, params);
string memory gasUsed = vm.toString(beforeGas - gasleft());

contentToAppend = string.concat(
"| `createWithDurationsLT` | Lockup Tranched |",
vm.toString(tranchesCount),
" |",
vm.toString(batchsize),
" | ",
gasUsed,
" |"
);

// Append the content to the file.
appendToFile(benchmarkResultsFile, contentToAppend);
}

function gasCreateWithTimestampsLT(uint256 batchsize, uint256 tranchesCount) internal {
BatchLockup.CreateWithTimestampsLT[] memory params = BatchLockupBuilder.fillBatch(
defaults.createWithTimestampsLT(dai, uint128(AMOUNT_PER_COUNT * tranchesCount), _tranches(tranchesCount)),
batchsize
);

uint256 beforeGas = gasleft();
batchLockup.createWithTimestampsLT(lockupTranched, dai, params);
string memory gasUsed = vm.toString(beforeGas - gasleft());

contentToAppend = string.concat(
"| `createWithTimestampsLT` | Lockup Tranched |",
vm.toString(tranchesCount),
" |",
vm.toString(batchsize),
" | ",
gasUsed,
" |"
);

// Append the data to the file
appendToFile(benchmarkResultsFile, contentToAppend);
}

/*//////////////////////////////////////////////////////////////////////////
HELPERS
//////////////////////////////////////////////////////////////////////////*/

function _segments(uint256 segmentsCount) private view returns (LockupDynamic.Segment[] memory) {
LockupDynamic.Segment[] memory segments = new LockupDynamic.Segment[](segmentsCount);

// Populate segments.
for (uint256 i = 0; i < segmentsCount; ++i) {
segments[i] = LockupDynamic.Segment({
amount: AMOUNT_PER_COUNT,
exponent: ud2x18(0.5e18),
timestamp: getBlockTimestamp() + uint40(defaults.CLIFF_DURATION() * (1 + i))
});
}

return segments;
}

function _segmentsWithDuration(uint256 segmentsCount)
private
view
returns (LockupDynamic.SegmentWithDuration[] memory)
{
LockupDynamic.SegmentWithDuration[] memory segments = new LockupDynamic.SegmentWithDuration[](segmentsCount);

// Populate segments.
for (uint256 i; i < segmentsCount; ++i) {
segments[i] = LockupDynamic.SegmentWithDuration({
amount: AMOUNT_PER_COUNT,
exponent: ud2x18(0.5e18),
duration: defaults.CLIFF_DURATION()
});
}

return segments;
}

function _tranches(uint256 tranchesCount) private view returns (LockupTranched.Tranche[] memory) {
LockupTranched.Tranche[] memory tranches = new LockupTranched.Tranche[](tranchesCount);
// Populate tranches.
for (uint256 i = 0; i < tranchesCount; ++i) {
tranches[i] = (
LockupTranched.Tranche({
amount: AMOUNT_PER_COUNT,
timestamp: getBlockTimestamp() + uint40(defaults.CLIFF_DURATION() * (1 + i))
})
);
}

return tranches;
}

function _tranchesWithDuration(uint256 tranchesCount)
private
view
returns (LockupTranched.TrancheWithDuration[] memory)
{
LockupTranched.TrancheWithDuration[] memory tranches = new LockupTranched.TrancheWithDuration[](tranchesCount);

// Populate tranches.
for (uint256 i; i < tranchesCount; ++i) {
tranches[i] =
LockupTranched.TrancheWithDuration({ amount: AMOUNT_PER_COUNT, duration: defaults.CLIFF_DURATION() });
}

return tranches;
}
}
54 changes: 54 additions & 0 deletions benchmark/Benchmark.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.8.22;

import { Integration_Test } from "../test/integration/Integration.t.sol";

/// @notice Benchmark contract with common logic needed by all tests.
abstract contract Benchmark_Test is Integration_Test {
/*//////////////////////////////////////////////////////////////////////////
STATE VARIABLES
//////////////////////////////////////////////////////////////////////////*/

/// @dev The directory where the benchmark files are stored.
string internal benchmarkResults = "benchmark/results/";

/// @dev The path to the file where the benchmark results are stored.
string internal benchmarkResultsFile;

string internal contentToAppend;

/*//////////////////////////////////////////////////////////////////////////
SET-UP FUNCTION
//////////////////////////////////////////////////////////////////////////*/

function setUp() public override {
super.setUp();

deal({ token: address(dai), to: users.alice, give: type(uint256).max });
resetPrank({ msgSender: users.alice });

// Create the first streams in each Lockup contract to initialize all the variables.
_createFewStreams();
}

/*//////////////////////////////////////////////////////////////////////////
HELPERS
//////////////////////////////////////////////////////////////////////////*/

/// @dev Append a line to the file at given path.
function appendToFile(string memory path, string memory line) internal {
vm.writeLine({ path: path, data: line });
}

/// @dev Internal function to creates a few streams in each Lockup contract.
function _createFewStreams() private {
approveContract({ asset_: dai, from: users.alice, spender: address(lockupDynamic) });
approveContract({ asset_: dai, from: users.alice, spender: address(lockupLinear) });
approveContract({ asset_: dai, from: users.alice, spender: address(lockupTranched) });
for (uint128 i = 0; i < 100; ++i) {
lockupDynamic.createWithTimestamps(defaults.createWithTimestampsLD());
lockupLinear.createWithTimestamps(defaults.createWithTimestampsLL());
lockupTranched.createWithTimestamps(defaults.createWithTimestampsLT());
}
}
}
Loading