Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit a4fd7c5

Browse files
committed
A script to summarize gas differences from isoltest for PRs.
1 parent 7d8a4e6 commit a4fd7c5

File tree

2 files changed

+142
-2
lines changed

2 files changed

+142
-2
lines changed

.circleci/config.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -354,8 +354,8 @@ jobs:
354354
command: apt -q update && apt install -y python3-pip
355355
- run:
356356
name: Install pylint
357-
command: python3 -m pip install pylint z3-solver pygments-lexer-solidity
358-
# also z3-solver to make sure pylint knows about this module, pygments-lexer-solidity for docs
357+
command: python3 -m pip install pylint z3-solver pygments-lexer-solidity parsec tabulate
358+
# also z3-solver, parsec and tabulate to make sure pylint knows about this module, pygments-lexer-solidity for docs
359359
- run:
360360
name: Linting Python Scripts
361361
command: ./scripts/pylint_all.py

scripts/gas_diff_stats.py

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
"""A script to collect gas statistics and print it.
2+
3+
Useful to summarize gas differences to semantic tests for a PR / branch.
4+
5+
Dependencies: Parsec (https://pypi.org/project/parsec/) and Tabulate
6+
(https://pypi.org/project/tabulate/)
7+
8+
pip install parsec tabulate
9+
10+
Run from root project dir.
11+
12+
python3 scripts/gas_diff_stats.py
13+
14+
Note that the changes to semantic tests have to be committed.
15+
16+
Assumes that there is a remote named ``origin`` pointing to the Solidity github
17+
repository. The changes are compared against ``origin/develop``.
18+
19+
"""
20+
import subprocess
21+
from pathlib import Path
22+
from enum import Enum
23+
from parsec import *
24+
from tabulate import tabulate
25+
26+
class Kind(Enum):
27+
IrOptimized = 1
28+
Legacy = 2
29+
LegacyOptimized = 3
30+
31+
class Diff(Enum):
32+
Minus = 1
33+
Plus = 2
34+
35+
minus = string("-").result(Diff.Minus)
36+
plus = string("+").result(Diff.Plus)
37+
38+
space = string(" ")
39+
comment = string("//")
40+
colon = string(":")
41+
42+
gas_ir_optimized = string("gas irOptimized").result(Kind.IrOptimized)
43+
gas_legacy_optimized = string("gas legacyOptimized").result(Kind.LegacyOptimized)
44+
gas_legacy = string("gas legacy").result(Kind.Legacy)
45+
46+
def number() -> int:
47+
"""Parse number."""
48+
return regex(r"([0-9]*)").parsecmap(int)
49+
50+
@generate
51+
def diff_string() -> (Kind, Diff, int):
52+
"""Usage: diff_string.parse(string)
53+
54+
Example string:
55+
56+
-// gas irOptimized: 138070
57+
58+
"""
59+
diff_kind = yield (minus | plus)
60+
yield comment
61+
yield space
62+
codegen_kind = yield (gas_ir_optimized ^ gas_legacy_optimized ^ gas_legacy)
63+
yield colon
64+
yield space
65+
val = yield number()
66+
return (diff_kind, codegen_kind, val)
67+
68+
def collect_statistics(lines) -> (int, int, int, int, int, int):
69+
"""Returns
70+
71+
(old_ir_optimized, old_legacy_optimized, old_legacy, new_ir_optimized,
72+
new_legacy_optimized, new_legacy)
73+
74+
All the values in the same file (in the diff) are summed up.
75+
76+
"""
77+
if not lines:
78+
raise Exception("Empty list")
79+
80+
def try_parse(line):
81+
try:
82+
return diff_string.parse(line)
83+
except ParseError:
84+
pass
85+
return None
86+
87+
out = [parsed for line in lines if (parsed := try_parse(line)) is not None]
88+
diff_kinds = [Diff.Minus, Diff.Plus]
89+
codegen_kinds = [Kind.IrOptimized, Kind.LegacyOptimized, Kind.Legacy]
90+
return tuple(
91+
sum([
92+
val
93+
for (diff_kind, codegen_kind, val) in out
94+
if diff_kind == _diff_kind and codegen_kind == _codegen_kind
95+
])
96+
for _diff_kind in diff_kinds
97+
for _codegen_kind in codegen_kinds
98+
)
99+
100+
def semantictest_statistics():
101+
"""Prints the tabulated statistics that can be pasted in github."""
102+
def try_parse_git_diff(fname):
103+
try:
104+
diff_output = subprocess.check_output(
105+
"git diff --unified=0 origin/develop HEAD " + fname,
106+
shell=True,
107+
universal_newlines=True
108+
).splitlines()
109+
if diff_output:
110+
return collect_statistics(diff_output)
111+
except subprocess.CalledProcessError as e:
112+
print("Error in the git diff:")
113+
print(e.output)
114+
return None
115+
def stat(old, new):
116+
return ((new - old) / old) * 100 if old else 0
117+
118+
table = []
119+
120+
for path in Path("test/libsolidity/semanticTests").rglob("*.sol"):
121+
fname = path.as_posix()
122+
parsed = try_parse_git_diff(fname)
123+
if parsed is None:
124+
continue
125+
ir_optimized = stat(parsed[0], parsed[3])
126+
legacy_optimized = stat(parsed[1], parsed[4])
127+
legacy = stat(parsed[2], parsed[5])
128+
fname = fname.split('/', 3)[-1]
129+
table += [map(str, [fname, ir_optimized, legacy_optimized, legacy])]
130+
131+
if table:
132+
print("<details><summary>Click for a table of gas differences</summary>\n")
133+
table_header = ["File name", "IR-optimized (%)", "Legacy-Optimized (%)", "Legacy (%)"]
134+
print(tabulate(table, headers=table_header, tablefmt="github"))
135+
print("</details>")
136+
else:
137+
print("No differences found.")
138+
139+
if __name__ == "__main__":
140+
semantictest_statistics()

0 commit comments

Comments
 (0)