Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
M
micropython
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Analytics
Analytics
Repository
Value Stream
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Commits
Open sidebar
xpstem
micropython
Commits
35a056ad
Commit
35a056ad
authored
Jul 23, 2024
by
Angus Gratton
Committed by
Damien George
Aug 14, 2024
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
esp32/tools: Add metrics_esp32 size comparison script.
Signed-off-by:
Angus Gratton
<
angus@redyak.com.au
>
parent
10601b04
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
192 additions
and
0 deletions
+192
-0
ports/esp32/tools/metrics_esp32.py
ports/esp32/tools/metrics_esp32.py
+192
-0
No files found.
ports/esp32/tools/metrics_esp32.py
0 → 100755
View file @
35a056ad
#!/usr/bin/env python
# MIT license; Copyright (c) 2024 Angus Gratton
#
# This is a utility script for MicroPython maintainers, similar to tools/metrics.py
# but particular to this port. It's for measuring the impact of an ESP-IDF update or
# config change at a high level.
#
# Specifically, it builds the esp32 MicroPython port for a collection of boards
# and outputs a Markdown table of binary sizes, static IRAM size, and static
# DRAM size (the latter generally inversely correlates to free heap at runtime.)
#
# To use:
#
# 1) Need to not be in an ESP-IDF venv already (i.e. don't source export.sh),
# but IDF_PATH has to be set.
#
# 2) Choose the versions you want to test and the board/variant pairs by
# editing the tuples below.
#
# 3) The IDF install script sometimes fails if it has to downgrade a package
# within a minor version. The "nuclear option" is to delete all the install
# environments and have this script recreate them as it runs:
# rm -rf ~/.espressif/python_env/*
#
# 4) Run this script from the ports/esp32 directory, i.e.:
# ./tools/metrics_esp32.py
#
# 5) If all goes well, it will run for a while and then print a Markdown
# formatted table of binary sizes, sorted by board+variant.
#
# Note that for ESP32-S3 and C3, IRAM and DRAM are exchangeable so the IRAM size
# column of the table is really D/IRAM.
import
os
import
re
import
sys
import
subprocess
from
dataclasses
import
dataclass
IDF_VERS
=
(
"v5.2.2"
,)
BUILDS
=
(
(
"ESP32_GENERIC"
,
""
),
(
"ESP32_GENERIC"
,
"D2WD"
),
(
"ESP32_GENERIC"
,
"SPIRAM"
),
(
"ESP32_GENERIC_S3"
,
""
),
(
"ESP32_GENERIC_S3"
,
"SPIRAM_OCT"
),
)
@
dataclass
class
BuildSizes
:
idf_ver
:
str
board
:
str
variant
:
str
bin_size
:
str
=
""
dram_size
:
str
=
""
iram_size
:
str
=
""
def
print_summary
(
self
,
include_ver
=
False
):
print
(
f"BOARD=
{
self
.
board
}
BOARD_VARIANT=
{
self
.
variant
}
"
)
if
include_ver
:
print
(
f"IDF_VER
{
self
.
idf_ver
}
"
)
print
(
f"Binary size:
{
self
.
bin_size
}
"
)
print
(
f"IRAM size:
{
self
.
iram_size
}
"
)
print
(
f"DRAM size:
{
self
.
dram_size
}
"
)
def
print_table_heading
():
print
(
"| BOARD | BOARD_VARIANT | IDF Version | Binary Size | Static IRAM Size | Static DRAM Size |"
)
print
(
"|-------|---------------|-------------|-------------|------------------|------------------|"
)
def
print_table_row
(
self
,
print_board
):
print
(
"| "
+
" | "
.
join
(
(
self
.
board
if
print_board
else
""
,
self
.
variant
if
print_board
else
""
,
self
.
idf_ver
,
self
.
bin_size
,
self
.
iram_size
,
self
.
dram_size
,
)
)
+
" |"
)
def
__lt__
(
self
,
other
):
"""sort by board, then variant, then IDF version to get an easy
to compare table"""
return
(
self
.
board
,
self
.
variant
,
self
.
idf_ver
)
<
(
other
.
board
,
other
.
variant
,
other
.
idf_ver
,
)
def
build_dir
(
self
):
if
self
.
variant
:
return
f"build-
{
self
.
board
}
_
{
self
.
variant
}
"
else
:
return
f"build-
{
self
.
board
}
"
def
run_make
(
self
,
target
):
env
=
dict
(
os
.
environ
)
env
[
"BOARD"
]
=
self
.
board
env
[
"BOARD_VARIANT"
]
=
self
.
variant
try
:
# IDF version changes as we go, so re-export the environment each time
cmd
=
f"source $IDF_PATH/export.sh; make
{
target
}
"
return
subprocess
.
check_output
(
cmd
,
shell
=
True
,
env
=
env
,
stderr
=
subprocess
.
STDOUT
).
decode
()
except
subprocess
.
CalledProcessError
as
e
:
err_file
=
f"
{
self
.
build_dir
()
}
/make-
{
target
}
-failed-
{
self
.
idf_ver
}
.log"
print
(
f"'make
{
target
}
' failed, writing to log to
{
err_file
}
"
,
file
=
sys
.
stderr
)
with
open
(
err_file
,
"w"
)
as
f
:
f
.
write
(
e
.
output
.
decode
())
raise
def
make_size
(
self
):
try
:
size_out
=
self
.
run_make
(
"size"
)
# "Used static DRAM:" or "Used stat D/IRAM:"
RE_DRAM
=
r"Used stat(?:ic)? D.*: *(\d+) bytes"
RE_IRAM
=
r"Used static IRAM: *(\d+) bytes"
RE_BIN
=
r"Total image size: *(\d+) bytes"
self
.
dram_size
=
re
.
search
(
RE_DRAM
,
size_out
).
group
(
1
)
self
.
iram_size
=
re
.
search
(
RE_IRAM
,
size_out
).
group
(
1
)
self
.
bin_size
=
re
.
search
(
RE_BIN
,
size_out
).
group
(
1
)
except
subprocess
.
CalledProcessError
:
self
.
bin_size
=
"build failed"
def
main
(
do_clean
):
if
"IDF_PATH"
not
in
os
.
environ
:
raise
RuntimeError
(
"IDF_PATH must be set"
)
sizes
=
[]
for
idf_ver
in
IDF_VERS
:
switch_ver
(
idf_ver
)
for
board
,
variant
in
BUILDS
:
print
(
f"Building '
{
board
}
'/'
{
variant
}
'..."
,
file
=
sys
.
stderr
)
result
=
BuildSizes
(
idf_ver
,
board
,
variant
)
result
.
run_make
(
"clean"
)
result
.
make_size
()
result
.
print_summary
()
sizes
.
append
(
result
)
# print everything again as a table sorted by board+variant
last_bv
=
""
BuildSizes
.
print_table_heading
()
for
build_sizes
in
sorted
(
sizes
):
bv
=
(
build_sizes
.
board
,
build_sizes
.
variant
)
build_sizes
.
print_table_row
(
last_bv
!=
bv
)
last_bv
=
bv
def
idf_git
(
*
commands
):
try
:
subprocess
.
check_output
(
[
"git"
]
+
list
(
commands
),
cwd
=
os
.
environ
[
"IDF_PATH"
],
stderr
=
subprocess
.
STDOUT
)
except
subprocess
.
CalledProcessError
as
e
:
print
(
f"git
{
' '
.
join
(
commands
)
}
failed:"
)
print
(
e
.
output
.
decode
())
raise
def
idf_install
():
try
:
subprocess
.
check_output
(
[
"bash"
,
"install.sh"
],
cwd
=
os
.
environ
[
"IDF_PATH"
],
stderr
=
subprocess
.
STDOUT
)
except
subprocess
.
CalledProcessError
as
e
:
print
(
"IDF install.sh failed:"
)
print
(
e
.
output
.
decode
())
raise
def
switch_ver
(
idf_ver
):
print
(
f"Switching version to
{
idf_ver
}
..."
,
file
=
sys
.
stderr
)
idf_git
(
"switch"
,
"--detach"
,
idf_ver
)
idf_git
(
"submodule"
,
"update"
,
"--init"
,
"--recursive"
)
idf_install
()
if
__name__
==
"__main__"
:
main
(
"--no-clean"
not
in
sys
.
argv
)
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment