From ab64916c379a145b57d21b95e4702bebf91747f3 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 12 Apr 2026 18:32:03 -1000 Subject: [PATCH 1/4] [benchmark] Use -Os to match firmware optimization level MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CodSpeed benchmarks were building with -O2, while all firmware targets (ESP8266, ESP32, LibreTiny) use -Os. This mismatch means the benchmarks cannot detect inlining regressions that affect real devices — GCC under -O2 inlines functions that -Os outlines due to its size-conscious cost model. Remove the -Os unflag and -O2 override so benchmarks use the platform default -Os, matching what actually runs on devices. --- script/cpp_benchmark.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/script/cpp_benchmark.py b/script/cpp_benchmark.py index 92faa05819a..c09c4dee904 100755 --- a/script/cpp_benchmark.py +++ b/script/cpp_benchmark.py @@ -26,11 +26,7 @@ CORE_BENCHMARKS_DIR: Path = Path(root_path) / "tests" / "benchmarks" / "core" STUBS_DIR: Path = Path(root_path) / "tests" / "benchmarks" / "stubs" PLATFORMIO_OPTIONS = { - "build_unflags": [ - "-Os", # remove default size-opt - ], "build_flags": [ - "-O2", # optimize for speed (CodSpeed recommends RelWithDebInfo) "-g", # debug symbols for profiling "-DUSE_BENCHMARK", # disable WarnIfComponentBlockingGuard in finish() f"-I{STUBS_DIR}", # stub headers for ESP32-only components From 02f828fcbf425d921023c1405b0528c78366a2fc Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 12 Apr 2026 18:37:50 -1000 Subject: [PATCH 2/4] [benchmark] Use -Os to match firmware optimization level MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CodSpeed benchmarks were building with -O2, while all firmware targets (ESP8266, ESP32, LibreTiny) use -Os. This mismatch means the benchmarks cannot detect inlining regressions that affect real devices — GCC under -O2 inlines functions that -Os outlines due to its size-conscious cost model. Switch to -Os with -ffunction-sections/-fdata-sections for proper dead-code stripping (needed because -Os preserves references that -O2 optimizes away at compile time). --- script/cpp_benchmark.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/script/cpp_benchmark.py b/script/cpp_benchmark.py index c09c4dee904..5080a9fec74 100755 --- a/script/cpp_benchmark.py +++ b/script/cpp_benchmark.py @@ -27,7 +27,10 @@ STUBS_DIR: Path = Path(root_path) / "tests" / "benchmarks" / "stubs" PLATFORMIO_OPTIONS = { "build_flags": [ + "-Os", # match firmware optimization level (detects inlining regressions) "-g", # debug symbols for profiling + "-ffunction-sections", # required for dead-code stripping with -Os + "-fdata-sections", # required for dead-code stripping with -Os "-DUSE_BENCHMARK", # disable WarnIfComponentBlockingGuard in finish() f"-I{STUBS_DIR}", # stub headers for ESP32-only components ], From 5a250cc74f0ee3583419ef3050346e050047cda0 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 12 Apr 2026 19:03:21 -1000 Subject: [PATCH 3/4] [api] Compile noise-c and libsodium with -O2 for speed Crypto libraries are CPU-bound and benefit significantly from speed optimization over the default -Os. Add a post: extra_script that appends -O2 to noise-c and libsodium build flags when API noise encryption is enabled. GCC uses the last -O flag, so this overrides the global -Os for these libraries only. --- esphome/components/api/__init__.py | 16 ++++++++++++++++ esphome/components/api/crypto_optimize.py.script | 9 +++++++++ 2 files changed, 25 insertions(+) create mode 100644 esphome/components/api/crypto_optimize.py.script diff --git a/esphome/components/api/__init__.py b/esphome/components/api/__init__.py index 84589d540d3..43677ebfdce 100644 --- a/esphome/components/api/__init__.py +++ b/esphome/components/api/__init__.py @@ -1,5 +1,6 @@ import base64 import logging +import pathlib from esphome import automation from esphome.automation import Condition @@ -458,6 +459,10 @@ async def to_code(config: ConfigType) -> None: # Enable optimized memzero/memcmp in libsodium instead of volatile byte loops cg.add_build_flag("-DHAVE_WEAK_SYMBOLS=1") cg.add_build_flag("-DHAVE_INLINE_ASM=1") + # Compile crypto libraries with -O2 for speed instead of -Os. + # Crypto is CPU-bound and benefits significantly from speed optimization. + # GCC uses the last -O flag, so appending -O2 overrides the global -Os. + _write_crypto_optimize_script() else: cg.add_define("USE_API_PLAINTEXT") @@ -465,6 +470,17 @@ async def to_code(config: ConfigType) -> None: cg.add_global(api_ns.using) +_CRYPTO_OPTIMIZE_SCRIPT = "crypto_optimize.py" + + +def _write_crypto_optimize_script() -> None: + from esphome.helpers import copy_file_if_changed + + script_src = pathlib.Path(__file__).parent / f"{_CRYPTO_OPTIMIZE_SCRIPT}.script" + copy_file_if_changed(script_src, CORE.relative_build_path(_CRYPTO_OPTIMIZE_SCRIPT)) + cg.add_platformio_option("extra_scripts", [f"post:{_CRYPTO_OPTIMIZE_SCRIPT}"]) + + KEY_VALUE_SCHEMA = cv.Schema({cv.string: cv.templatable(cv.string_strict)}) diff --git a/esphome/components/api/crypto_optimize.py.script b/esphome/components/api/crypto_optimize.py.script new file mode 100644 index 00000000000..fe693d19117 --- /dev/null +++ b/esphome/components/api/crypto_optimize.py.script @@ -0,0 +1,9 @@ +# Compile crypto libraries with -O2 for speed instead of the default -Os. +# Crypto is CPU-bound and benefits significantly from speed optimization. +# GCC uses the last -O flag, so appending -O2 overrides the global -Os +# for these libraries only. +Import("env") + +for lb in env.GetLibBuilders(): + if lb.name in ("noise-c", "libsodium"): + lb.env.Append(CCFLAGS=["-O2"]) From 63184e95a2d8ba53bceae10b57e39cd4b47a2b27 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 12 Apr 2026 19:15:06 -1000 Subject: [PATCH 4/4] [api] Only apply -O2 to libsodium, not noise-c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CodSpeed results show only Noise benchmarks improved with -O2 — the speedup comes from libsodium's crypto primitives (Curve25519, ChaCha20, Poly1305), not noise-c's protocol layer. Narrow the optimization to libsodium only. --- esphome/components/api/crypto_optimize.py.script | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/esphome/components/api/crypto_optimize.py.script b/esphome/components/api/crypto_optimize.py.script index fe693d19117..1093a09bf6a 100644 --- a/esphome/components/api/crypto_optimize.py.script +++ b/esphome/components/api/crypto_optimize.py.script @@ -1,9 +1,9 @@ -# Compile crypto libraries with -O2 for speed instead of the default -Os. -# Crypto is CPU-bound and benefits significantly from speed optimization. -# GCC uses the last -O flag, so appending -O2 overrides the global -Os -# for these libraries only. +# Compile libsodium with -O2 for speed instead of the default -Os. +# libsodium provides the crypto primitives (Curve25519, ChaCha20, Poly1305) +# used by the Noise protocol and benefits significantly from speed optimization. +# GCC uses the last -O flag, so appending -O2 overrides the global -Os. Import("env") for lb in env.GetLibBuilders(): - if lb.name in ("noise-c", "libsodium"): + if lb.name == "libsodium": lb.env.Append(CCFLAGS=["-O2"])