10BC0 esp32: Add maintainer size comparison script. · micropython/micropython@7df6779 · GitHub
[go: up one dir, main page]

Skip to content

Commit 7df6779

Browse files
committed
esp32: Add maintainer size comparison script.
Signed-off-by: Angus Gratton <angus@redyak.com.au>
1 parent 32a355d commit 7df6779

File tree

1 file changed

+187
-0
lines changed

1 file changed

+187
-0
lines changed
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
#!/usr/bin/env python
2+
# MIT license; Copyright (c) 2024 Angus Gratton
3+
#
4+
# This is a utility script for MicroPython maintainers. It builds the esp32
5+
# MicroPython port for a collection of boards and outputs a table of binary
6+
# sizes, static IRAM size, and static DRAM size (which generally inversely
7+
# correlates to free heap at runtime.)
8+
#
9+
# It's for measuring the impact of an ESP-IDF update, at a high level.
10+
#
11+
# To use:
12+
# 1) Need to not be in an ESP-IDF venv already (i.e. don't source export.sh),
13+
# but IDF_PATH has to be set.
14+
#
15+
# 2) Choose the versions you want to test and the board/variant pairs by
16+
# editing the tuples below.
17+
#
18+
# 3) The IDF install script sometimes fails if it has to downgrade a package
19+
# within a minor version. The "nuclear option" is to delete all the install
20+
# environments and have this script recreate them as it runs:
21+
# rm -rf /home/gus/.espressif/python_env/*
22+
#
23+
# 4) Run this script from the ports/esp32 directory, i.e.:
24+
# ./tools/build_size_comparison.py
25+
#
26+
# 5) If all goes well, it will run for a while and then print a Markdown
27+
# formatted table of binary sizes, sorted by board+variant.
28+
#
29+
# Note that for ESP32-S3 and C3, IRAM and DRAM are exchangeable so the IRAM size
30+
# column of the table is really D/IRAM.
31+
import os
32+
import re
33+
import sys
34+
import subprocess
35+
from dataclasses import dataclass
36+
37+
IDF_VERS = ("v5.2.2",)
38+
39+
BUILDS = (
40+
("ESP32_GENERIC", ""),
41+
("ESP32_GENERIC", "D2WD"),
42+
("ESP32_GENERIC", "SPIRAM"),
43+
("ESP32_GENERIC_S3", ""),
44+
("ESP32_GENERIC_S3", "SPIRAM_OCT"),
45+
)
46+
47+
48+
@dataclass
49+
class BuildSizes:
50+
idf_ver: str
51+
board: str
52+
variant: str
53+
bin_size: str = ""
54+
dram_size: str = ""
55+
iram_size: str = ""
56+
57+
def print_summary(self, include_ver=False):
58+
print(f"BOARD={self.board} BOARD_VARIANT={self.variant}")
59+
if include_ver:
60+
print(f"IDF_VER {self.idf_ver}")
61+
print(f"Binary size: {self.bin_size}")
62+
print(f"IRAM size: {self.iram_size}")
63+
print(f"DRAM size: {self.dram_size}")
64+
65+
def print_table_heading():
66+
print(
67+
"| BOARD | BOARD_VARIANT | IDF Version | Binary Size | Static IRAM Size | Static DRAM Size |"
68+
)
69+
print(
70+
"|-------|---------------|-------------|-------------|------------------|------------------|"
71+
)
72+
73+
def print_table_row(self, print_board):
74+
print(
75+
"| "
76+
+ " | ".join(
77+
(
78+
self.board if print_board else "",
79+
self.variant if print_board else "",
80+
self.idf_ver,
81+
self.bin_size,
82+
self.iram_size,
83+
self.dram_size,
84+
)
85+
)
86+
+ " |"
87+
)
88+
89+
def __lt__(self, other):
90+
"""sort by board, then variant, then IDF version to get an easy
91+
to compare table"""
92+
return (self.board, self.variant, self.idf_ver) < (
93+
other.board,
94+
other.variant,
95+
other.idf_ver,
96+
)
97+
98+
def build_dir(self):
99+
if self.variant:
100+
return f"build-{self.board}_{self.variant}"
101+
else:
102+
return f"build-{self.board}"
103+
104+
def run_make(self, target):
105+
env = dict(os.environ)
106+
env["BOARD"] = self.board
107+
env["BOARD_VARIANT"] = self.variant
108+
109+
try:
110+
# IDF version changes as we go, so re-export the environment each time
111+
cmd = f"source $IDF_PATH/export.sh; make {target}"
112+
return subprocess.check_output(
113+
cmd, shell=True, env=env, stderr=subprocess.STDOUT
114+
).decode()
115+
except subprocess.CalledProcessError as e:
116+
err_file = f"{self.build_dir()}/make-{target}-failed-{self.idf_ver}.log"
117+
print(f"'make {target}' failed, writing to log to {err_file}", file=sys.stderr)
118+
with open(err_file, "w") as f:
119+
f.write(e.output.decode())
120+
raise
121+
122+
def make_size(self):
123+
try:
124+
size_out = self.run_make("size")
125+
# this line matches either "Used static DRAM:" or "Used stat D/IRAM:"
126+
self.dram_size = re.search(r"Used stati?c? D.*: *(\d+) bytes", size_out).group(1)
127+
self.iram_size = re.search(r"Used static IRAM: *(\d+) bytes", size_out).group(1)
128+
self.bin_size = re.search(r"Total image size: *(\d+) bytes", size_out).group(1)
129+
except subprocess.CalledProcessError:
130+
self.bin_size = "build failed"
131+
132+
133+
def main(do_clean):
134+
if "IDF_PATH" not in os.environ:
135+
raise RuntimeError("IDF_PATH must be set")
136+
137+
sizes = []
138+
for idf_ver in IDF_VERS:
139+
switch_ver(idf_ver)
140+
for board, variant in BUILDS:
141+
print(f"Building '{board}'/'{variant}'...", file=sys.stderr)
142+
result = BuildSizes(idf_ver, board, variant)
143+
result.run_make("clean")
144+
result.make_size()
145+
result.print_summary()
146+
sizes.append(result)
147+
148+
# print everything again as a table sorted by board+variant
149+
last_bv = ""
150+
BuildSizes.print_table_heading()
151+
for build_sizes in sorted(sizes):
152+
bv = (build_sizes.board, build_sizes.variant)
153+
build_sizes.print_table_row(last_bv != bv)
154+
last_bv = bv
155+
156+
157+
def idf_git(*commands):
158+
try:
159+
subprocess.check_output(
160+
["git"] + list(commands), cwd=os.environ["IDF_PATH"], stderr=subprocess.STDOUT
161+
)
162+
except subprocess.CalledProcessError as e:
163+
print(f"git {' '.join(commands)} failed:")
164+
print(e.output.decode())
165+
raise
166+
167+
168+
def idf_install():
169+
try:
170+
subprocess.check_output(
171+
["bash", "install.sh"], cwd=os.environ["IDF_PATH"], stderr=subprocess.STDOUT
172+
)
173+
except subprocess.CalledProcessError as e:
174+
print("IDF install.sh failed:")
175+
print(e.output.decode())
176+
raise
177+
178+
179+
def switch_ver(idf_ver):
180+
print(f"Switching version to {idf_ver}...", file=sys.stderr)
181+
idf_git("switch", "--detach", idf_ver)
182+
idf_git("submodule", "update", "--init", "--recursive")
183+
idf_install()
184+
185+
186+
if __name__ == "__main__":
187+
main("--no-clean" not in sys.argv)

0 commit comments

Comments
 (0)
0