Skip to content

Commit be82c67

Browse files
authored
refactor: pythonise benchmark file generation (#16029)
Altering the solidity benchmark file generation such that it is simpler to follow by not using `awk` but also making it a bit more elaborate to produce results for both ignition and alpha configurations in one go, making it simpler to get an idea of the "real" costs and not "the costs that the benchmark config have". The configuration is added to the markdown file, and the gas per second is also added to both benchmark and markdown doc. I tried to group the benchmarks into different configs so they hopefully are easier to jump around in at the benchmark page. Updated the l1 metadata loaded in benchmark slightly to better accomedate longer l2 block times without running out of data.
1 parent db50f5f commit be82c67

6 files changed

Lines changed: 1026 additions & 241 deletions

File tree

l1-contracts/bootstrap.sh

Lines changed: 25 additions & 198 deletions
Original file line numberDiff line numberDiff line change
@@ -145,106 +145,32 @@ function bench_cmds {
145145
function bench {
146146
rm -rf bench-out && mkdir -p bench-out
147147

148-
# Run the gas benchmark to generate the markdown file
148+
# Run the gas benchmark to generate the markdown file and JSON results
149149
gas_benchmark
150150

151-
# Extract gas values from gas_benchmark.md and create JSON output
152-
awk '
153-
function trim(s) {
154-
sub(/^[ \t]+/, "", s);
155-
sub(/[ \t]+$/, "", s);
156-
return s;
157-
}
158-
BEGIN {
159-
print "[";
160-
first = 1;
161-
}
162-
/^[a-zA-Z]/ {
163-
if ($1 != "Function" && $1 != "-------------------------") {
164-
# Split the line into columns and clean them
165-
n = split($0, cols, "|");
166-
for (i = 1; i <= n; i++) {
167-
cols[i] = trim(cols[i]);
168-
}
169-
170-
# Only process Max rows
171-
if (cols[2] == "Max") {
172-
# Get the function name
173-
func_name = cols[1];
174-
175-
# Define our cases with their column numbers
176-
cases["no_validators"] = 3;
177-
cases["100_validators"] = 4;
178-
cases["100_validators_slashing"] = 5;
179-
cases["overhead"] = 6;
180-
181-
for (case_name in cases) {
182-
col = cases[case_name];
183-
184-
# Filter: only include aggregate3 functions with 100_validators_slashing, and vice versa
185-
has_aggregate3 = index(tolower(func_name), "aggregate3") > 0;
186-
is_slashing_case = (case_name == "100_validators_slashing");
187-
188-
# Skip if aggregate3 function but not slashing case, or slashing case but not aggregate3 function
189-
if ((has_aggregate3 && !is_slashing_case) || (is_slashing_case && !has_aggregate3)) {
190-
continue;
191-
}
192-
193-
194-
# Rename aggregate3 to proposeAndVote in function name
195-
display_func_name = func_name;
196-
if (has_aggregate3) {
197-
gsub(/aggregate3/, "proposeAndVote", display_func_name);
198-
}
199-
200-
if (match(cols[col], /([0-9]+)[ ]*\(([0-9.]+)\)/)) {
201-
# Extract the raw gas value (first number)
202-
match(cols[col], /[0-9]+/);
203-
raw_gas = substr(cols[col], RSTART, RLENGTH);
204-
205-
# Extract the per tx value
206-
match(cols[col], /\(([0-9.]+)\)/);
207-
per_tx = substr(cols[col], RSTART+1, RLENGTH-2);
208-
209-
if (!first) print ",";
210-
first = 0;
211-
212-
# Output raw gas value
213-
print " {";
214-
print " \"name\": \"" display_func_name " (" case_name ")\",";
215-
print " \"value\": " raw_gas ",";
216-
print " \"unit\": \"gas\"";
217-
print " },";
218-
219-
# Output per tx value
220-
print " {";
221-
print " \"name\": \"" display_func_name " (" case_name ") per l2 tx\",";
222-
print " \"value\": " per_tx ",";
223-
print " \"unit\": \"gas\"";
224-
print " }";
225-
}
226-
}
227-
}
228-
}
229-
}
230-
END {
231-
print "]";
232-
}' gas_benchmark.md > ./bench-out/l1-gas.bench.json
151+
# Use Python script to generate the benchmark JSON from gas_benchmark_results.json
152+
python3 scripts/generate_benchmark_json.py
233153
}
234154

235155
function gas_benchmark {
236156
check=${1:-"no"}
237157

238-
validator_costs
158+
echo_header "l1-contracts gas benchmark"
159+
forge --version
239160

240-
diff gas_benchmark.new.md gas_benchmark.md > gas_benchmark.diff || true
161+
# Run the new Python benchmarking script
162+
echo "Running gas benchmarks..."
163+
python3 scripts/gas_benchmarks.py
241164

242-
if [ -s gas_benchmark.diff -a "$check" = "check" ]; then
243-
cat gas_benchmark.diff
244-
echo "Gas benchmark has changed. Please check the diffs above, then run './bootstrap.sh gas_benchmark' to update the gas benchmark."
245-
exit 1
165+
# The script generates gas_benchmark.md directly
166+
# Check if it differs from the committed version
167+
if [ "$check" = "check" ]; then
168+
if ! git diff --quiet gas_benchmark.md; then
169+
git diff gas_benchmark.md
170+
echo "Gas benchmark has changed. Please check the diffs above, then run './bootstrap.sh gas_benchmark' to update the gas benchmark."
171+
exit 1
172+
fi
246173
fi
247-
mv gas_benchmark.new.md gas_benchmark.md
248174
}
249175

250176
function validator_costs {
@@ -256,129 +182,30 @@ function validator_costs {
256182
--match-contract "BenchmarkRollupTest" \
257183
--match-test "test_no_validators" \
258184
--fuzz-seed 42 \
259-
> no_validators.tmp
185+
--json \
186+
> no_validators.json
260187

261188
# Run test with 100 validators
262189
echo "Running test with 100 validators..."
263190
FORGE_GAS_REPORT=true forge test \
264191
--match-contract "BenchmarkRollupTest" \
265192
--match-test "test_100_validators" \
266193
--fuzz-seed 42 \
267-
> with_validators.tmp
194+
--json \
195+
> 100_validators.json
268196

269197
# Run test with 100 validators and slashing
270198
echo "Running test with 100 validators and slashing..."
271199
FORGE_GAS_REPORT=true forge test \
272200
--match-contract "BenchmarkRollupTest" \
273201
--match-test "test_100_slashing_validators" \
274202
--fuzz-seed 42 \
275-
> with_slashing_validators.tmp
276-
277-
file_no="no_validators.tmp" # without validators
278-
file_yes="with_validators.tmp" # with validators
279-
file_yes_slashing="with_slashing_validators.tmp" # with validators and slashing
280-
report="gas_benchmark.new.md" # will be overwritten each run
281-
282-
# keep ONLY these functions, in this order
283-
wanted_funcs="propose setupEpoch submitEpochRootProof aggregate3"
284-
285-
# one label per numeric column (use | to separate)
286-
labels='Min|Avg|Median|Max|# Calls'
287-
288-
awk -v keep="$wanted_funcs" -v lbl="$labels" \
289-
-v f_no="$file_no" -v f_yes="$file_yes" -v f_yes_slashing="$file_yes_slashing" '
290-
function trim(s){gsub(/^[[:space:]]+|[[:space:]]+$/,"",s); return s}
291-
# cell(raw [, scaled])
292-
# If you call it with ONE argument, you get the raw value only.
293-
# If you call it with TWO arguments, you get "raw (scaled)" padded to 22.
294-
function cell(raw, scaled, s) {
295-
# Was a second parameter supplied?
296-
if ( scaled == "" ) # argument omitted → print raw only
297-
return sprintf("%22d", raw)
298-
299-
s = sprintf("%10d (%.2f)", raw, scaled)
300-
return sprintf("%-22s", s) # left-pad / truncate to 22 chars
301-
}
302-
303-
BEGIN{
304-
# ---------------- wanted functions & labels (unchanged) ---------------
305-
nf = split(keep, F, /[[:space:]]+/)
306-
for (i = 1; i <= nf; i++) { order[i] = F[i]; want[F[i]] }
307-
split(lbl, L, /\|/); nLab = length(L)
308-
309-
# ---------------- fixed-width formats ---------------------------------
310-
# header row
311-
hdr = "%-24s | %-7s | %22s | %23s | %22s | %12s\n"
312-
sep = "-------------------------+---------+------------------------+-------------------------+------------------------+-----------------"
313-
# data row (the three %22s will already be fully padded strings)
314-
row = "%-24s | %-7s | %22s | %23s | %22s | %10.2f%%\n"
315-
316-
printf hdr, "Function", "Metric",
317-
"No Validators (gas/tx)", "100 Validators (gas/tx)", "Δ Gas (gas/tx)", "% Overhead"
318-
print sep
319-
320-
FS="|"; OFS=""
321-
}
322-
# ---------- first file: without validators ----------------------------------
323-
FNR==NR {
324-
if($0 !~ /^\|/) next
325-
split($0, C) # C[1] "", C[2] fn, C[3..] numbers
326-
fn = trim(C[2])
327-
if(!(fn in want)) next
328-
for(i=3; i<=NF-1; i++) base[fn,i] = trim(C[i]) + 0
329-
cols[fn] = NF - 3 # remember how many numeric cols
330-
next
331-
}
332-
# ---------- second file: with validators ------------------------------------
333-
{
334-
if($0 !~ /^\|/) next
335-
split($0, C)
336-
fn = trim(C[2])
337-
if(!(fn in want)) next
338-
for(i=3; i<=NF-1; i++) with[fn,i] = trim(C[i]) + 0
339-
cols[fn] = NF - 3
340-
}
341-
# ---------- third file: with validators and slashing --------------------------
342-
{
343-
if($0 !~ /^\|/) next
344-
split($0, C)
345-
fn = trim(C[2])
346-
if(!(fn in want)) next
347-
for(i=3; i<=NF-1; i++) with_slashing[fn,i] = trim(C[i]) + 0
348-
cols[fn] = NF - 3
349-
}
350-
# ---------- emit table -------------------------------------------------------
351-
END{
352-
for (k = 1; k <= nf; k++) {
353-
fn = order[k]
354-
div = (fn == "propose" || fn == "aggregate3" ? 360 : 11520) # change 11520→720 if desired
355-
356-
for (j = 1; j <= cols[fn]; j++) {
357-
idx = j + 2
358-
metric = L[j]
359-
a = base[fn,idx] + 0
360-
b = with[fn,idx] + 0
361-
diff = b - a
362-
pct = (a ? diff * 100.0 / a : 0)
363-
364-
if (metric == "# Calls") {
365-
c1 = cell(a)
366-
c2 = cell(b)
367-
c3 = cell(diff)
368-
} else {
369-
c1 = cell(a, a/div)
370-
c2 = cell(b, b/div)
371-
c3 = cell(diff,diff/div)
372-
}
373-
printf row, fn, metric, c1, c2, c3, pct
374-
}
375-
print sep
376-
}
377-
}
378-
' "$file_no" "$file_yes" "$file_yes_slashing" > "$report"
203+
--json \
204+
> 100_validators_slashing.json
379205

380-
# Clean up temporary files
381-
rm no_validators.tmp with_validators.tmp with_slashing_validators.tmp
206+
# Use Python script to process the JSON files
207+
echo "Processing gas reports with Python script..."
208+
python3 scripts/process_gas_reports.py no_validators.json 100_validators.json 100_validators_slashing.json
382209
}
383210

384211
# First argument is a branch name (e.g. master, or the latest version e.g. 1.2.3) to push to the head of.

l1-contracts/gas_benchmark.md

Lines changed: 73 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,73 @@
1-
Function | Metric | No Validators (gas/tx) | 100 Validators (gas/tx) | Δ Gas (gas/tx) | % Overhead
2-
-------------------------+---------+------------------------+-------------------------+------------------------+-----------------
3-
propose | Min | 209098 (580.83) | 409354 (1137.09) | 200256 (556.27) | 95.77%
4-
propose | Avg | 219044 (608.46) | 419428 (1165.08) | 200384 (556.62) | 91.48%
5-
propose | Median | 219084 (608.57) | 419290 (1164.69) | 200206 (556.13) | 91.38%
6-
propose | Max | 235034 (652.87) | 435649 (1210.14) | 200615 (557.26) | 85.36%
7-
propose | # Calls | 100 | 100 | 0 | 0.00%
8-
-------------------------+---------+------------------------+-------------------------+------------------------+-----------------
9-
setupEpoch | Min | 37790 (3.28) | 37790 (3.28) | 0 (0.00) | 0.00%
10-
setupEpoch | Avg | 41912 (3.64) | 61658 (5.35) | 19746 (1.71) | 47.11%
11-
setupEpoch | Median | 39790 (3.45) | 39790 (3.45) | 0 (0.00) | 0.00%
12-
setupEpoch | Max | 106381 (9.23) | 600014 (52.08) | 493633 (42.85) | 464.02%
13-
setupEpoch | # Calls | 100 | 100 | 0 | 0.00%
14-
-------------------------+---------+------------------------+-------------------------+------------------------+-----------------
15-
submitEpochRootProof | Min | 654650 (56.83) | 654650 (56.83) | 0 (0.00) | 0.00%
16-
submitEpochRootProof | Avg | 674114 (58.52) | 674114 (58.52) | 0 (0.00) | 0.00%
17-
submitEpochRootProof | Median | 654746 (56.84) | 654746 (56.84) | 0 (0.00) | 0.00%
18-
submitEpochRootProof | Max | 712946 (61.89) | 712946 (61.89) | 0 (0.00) | 0.00%
19-
submitEpochRootProof | # Calls | 3 | 3 | 0 | 0.00%
20-
-------------------------+---------+------------------------+-------------------------+------------------------+-----------------
21-
aggregate3 | Min | 0 (0.00) | 454945 (1263.74) | 454945 (1263.74) | 0.00%
22-
aggregate3 | Avg | 0 (0.00) | 471529 (1309.80) | 471529 (1309.80) | 0.00%
23-
aggregate3 | Median | 0 (0.00) | 472537 (1312.60) | 472537 (1312.60) | 0.00%
24-
aggregate3 | Max | 0 (0.00) | 495790 (1377.19) | 495790 (1377.19) | 0.00%
25-
aggregate3 | # Calls | 0 | 100 | 100 | 0.00%
26-
-------------------------+---------+------------------------+-------------------------+------------------------+-----------------
1+
# Gas Benchmark Report
2+
3+
## IGNITION
4+
5+
### Configuration
6+
7+
| Parameter | Value |
8+
|-----------------------|-------|
9+
| Slot Duration | 60 |
10+
| Epoch Duration | 48 |
11+
| Target Committee Size | 24 |
12+
| Mana Target | 0 |
13+
| Proofs per Epoch | 2.00 |
14+
15+
### No Validators (IGNITION)
16+
17+
| Function | Avg | Max |
18+
|----------------------|---------|---------|
19+
| propose | 180,182 | 195,380 |
20+
| setupEpoch | 40,785 | 103,864 |
21+
| submitEpochRootProof | 799,356 | 819,960 |
22+
23+
**Avg Gas Cost per Second**: 3,572.3 gas/second
24+
*Epoch duration*: 0h 48m 0s
25+
26+
### Validators (IGNITION)
27+
28+
| Function | Avg | Max |
29+
|----------------------|---------|---------|
30+
| propose | 284,434 | 300,076 |
31+
| setupEpoch | 48,311 | 354,729 |
32+
| submitEpochRootProof | 799,356 | 819,960 |
33+
| aggregate3 | 331,654 | 351,870 |
34+
35+
**Avg Gas Cost per Second**: 5,312.4 gas/second
36+
*Epoch duration*: 0h 48m 0s
37+
38+
39+
## Alpha
40+
41+
### Configuration
42+
43+
| Parameter | Value |
44+
|-----------------------|-------------|
45+
| Slot Duration | 36 |
46+
| Epoch Duration | 32 |
47+
| Target Committee Size | 48 |
48+
| Mana Target | 100,000,000 |
49+
| Proofs per Epoch | 2.00 |
50+
51+
### No Validators (Alpha)
52+
53+
| Function | Avg | Max |
54+
|----------------------|---------|---------|
55+
| propose | 219,022 | 235,012 |
56+
| setupEpoch | 41,912 | 106,381 |
57+
| submitEpochRootProof | 671,999 | 710,831 |
58+
59+
**Avg Gas Cost per Second**: 7,287.0 gas/second
60+
*Epoch duration*: 0h 19m 12s
61+
62+
### Validators (Alpha)
63+
64+
| Function | Avg | Max |
65+
|----------------------|---------|---------|
66+
| propose | 419,406 | 435,627 |
67+
| setupEpoch | 61,658 | 600,014 |
68+
| submitEpochRootProof | 671,999 | 710,831 |
69+
| aggregate3 | 471,485 | 495,734 |
70+
71+
**Avg Gas Cost per Second**: 12,870.4 gas/second
72+
*Epoch duration*: 0h 19m 12s
73+

0 commit comments

Comments
 (0)