Compare commits

...

23 Commits

Author SHA1 Message Date
J. Nick Koston
566476b05f Merge branch 'dev' into app-loop-optimize-speed 2026-04-24 10:47:12 -05:00
pre-commit-ci-lite[bot]
adbbbe9cc5 [pre-commit.ci lite] apply automatic fixes 2026-04-21 03:59:07 +00:00
J. Nick Koston
46b0c9331b Apply suggestion from @bdraco 2026-04-21 05:57:35 +02:00
J. Nick Koston
b39ea3c19f Merge branch 'dev' into app-loop-optimize-speed 2026-04-21 05:13:14 +02:00
J. Nick Koston
051326e70e Merge remote-tracking branch 'upstream/dev' into app-loop-optimize-speed
# Conflicts:
#	esphome/core/application.h
2026-04-14 15:06:44 -10:00
J. Nick Koston
220f3d8142 Merge branch 'dev' into app-loop-optimize-speed 2026-04-13 17:08:24 -10:00
J. Nick Koston
7d0391aed6 Merge branch 'dev' into app-loop-optimize-speed 2026-04-13 08:44:55 -10:00
J. Nick Koston
9a9f9fa9f3 Merge branch 'dev' into app-loop-optimize-speed 2026-04-12 21:45:08 -10:00
J. Nick Koston
4dddccab6e Merge branch 'proto-speed-optimized-v2' into app-loop-optimize-speed 2026-04-12 20:34:39 -10:00
J. Nick Koston
6c115e4692 Merge branch 'benchmark-use-os-optimization' into proto-speed-optimized-v2 2026-04-12 20:24:51 -10:00
J. Nick Koston
ff26fe32c1 Merge branch 'proto-speed-optimized-v2' into app-loop-optimize-speed 2026-04-12 20:24:43 -10:00
J. Nick Koston
494f11ce77 Merge branch 'dev' into benchmark-use-os-optimization 2026-04-12 20:24:33 -10:00
pre-commit-ci-lite[bot]
a463e25aa1 [pre-commit.ci lite] apply automatic fixes 2026-04-13 06:23:08 +00:00
pre-commit-ci-lite[bot]
6aabada342 [pre-commit.ci lite] apply automatic fixes 2026-04-13 06:22:55 +00:00
J. Nick Koston
603d5a2b54 Fix clang-tidy NOLINT for optimize(O2) in generated protobuf code 2026-04-12 20:21:43 -10:00
J. Nick Koston
3e9f464a2c Fix clang-tidy and test for optimize(O2) attribute 2026-04-12 20:21:11 -10:00
J. Nick Koston
9a022baa06 [core] Optimize main loop with -O2
Add __attribute__((optimize("O2"))) to the main loop functions
(loop_task on ESP32, codegen loop() on other platforms) so GCC
inlines scheduler helpers and loop bookkeeping more aggressively.

Under -Os, GCC outlines small functions (Scheduler::call helpers,
millis conversions, etc.) that are called every loop iteration.
With -O2, these get inlined into the loop body, reducing call
overhead on the hottest code path in the firmware.

ESP32: loop_task grows from 303 to 416 bytes (+113 bytes).
ESP8266: no change (already fully inlined via ESPHOME_ALWAYS_INLINE).
2026-04-12 20:06:34 -10:00
J. Nick Koston
d9762759c0 Merge branch 'benchmark-use-os-optimization' into proto-speed-optimized-v2 2026-04-12 19:30:45 -10:00
J. Nick Koston
d217ab3cd4 [api] Add speed_optimized proto option for hot encode paths
Add a new (speed_optimized) message option that emits
__attribute__((optimize("O2"))) on the generated encode() and
calculate_size() methods. Under -Os, GCC does not inline the small
ProtoEncode helpers (write_raw_byte, encode_varint, etc.) into the
generated methods, causing significant overhead on hot paths.

Apply to SensorStateResponse and BluetoothLERawAdvertisementsResponse
which are the highest-frequency encode paths.
2026-04-12 19:23:39 -10:00
J. Nick Koston
70dd732821 [api] Add speed_optimized proto option for hot encode paths
Add a new (speed_optimized) message option that emits
__attribute__((optimize("O2"))) on the generated encode() and
calculate_size() methods. Under -Os, GCC does not inline the small
ProtoEncode helpers (write_raw_byte, encode_varint, etc.) into the
generated methods, causing significant overhead on hot paths.

Apply to SensorStateResponse and BluetoothLERawAdvertisementsResponse
which are the highest-frequency encode paths.
2026-04-12 19:21:47 -10:00
J. Nick Koston
9acfeec431 Merge branch 'dev' into benchmark-use-os-optimization 2026-04-12 18:40:32 -10:00
J. Nick Koston
02f828fcbf [benchmark] Use -Os to match firmware optimization level
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).
2026-04-12 18:37:50 -10:00
J. Nick Koston
ab64916c37 [benchmark] Use -Os to match firmware optimization level
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.
2026-04-12 18:32:03 -10:00
6 changed files with 11 additions and 6 deletions

View File

@@ -84,7 +84,8 @@ static StaticTask_t loop_task_tcb; // NOLINT(cppcoreguidelines-avoid-non-
static StackType_t
loop_task_stack[ESPHOME_LOOP_TASK_STACK_SIZE]; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
void loop_task(void *pv_params) {
void __attribute__((optimize("O2"))) // NOLINT(clang-diagnostic-unknown-attributes)
loop_task(void *pv_params) {
setup();
while (true) {
App.loop();

View File

@@ -77,7 +77,8 @@ uint32_t arch_get_cpu_freq_hz() { return 1000000000U; }
void setup();
void loop();
int main() {
int __attribute__((optimize("O2"))) // NOLINT(clang-diagnostic-unknown-attributes)
main() {
// Install signal handlers for graceful shutdown (flushes preferences to disk)
std::signal(SIGINT, signal_handler);
std::signal(SIGTERM, signal_handler);

View File

@@ -95,7 +95,8 @@ void get_mac_address_raw(uint8_t *mac) { // NOLINT(readability-non-const-parame
void setup();
void loop();
int main() {
int __attribute__((optimize("O2"))) // NOLINT(clang-diagnostic-unknown-attributes)
main() {
setup();
while (true) {
loop();

View File

@@ -541,7 +541,8 @@ inline ESPHOME_ALWAYS_INLINE Application::ComponentPhaseGuard::ComponentPhaseGua
this->app_.in_loop_ = true;
}
inline void ESPHOME_ALWAYS_INLINE Application::loop() {
inline void ESPHOME_ALWAYS_INLINE __attribute__((optimize("O2"))) // NOLINT(clang-diagnostic-unknown-attributes)
Application::loop() {
#ifdef USE_RUNTIME_STATS
// Capture the start of the active (non-sleeping) portion of this iteration.
// Used to derive main-loop overhead = active time Σ(component time)

View File

@@ -45,7 +45,7 @@ void setup() {
App.setup();
}
void loop() {
void __attribute__((optimize("O2"))) loop() {
App.loop();
}
""",

View File

@@ -782,7 +782,8 @@ def test_write_cpp_creates_new_file(
assert CPP_AUTO_GENERATE_END in written_content
assert test_code in written_content
assert "void setup()" in written_content
assert "void loop()" in written_content
assert 'optimize("O2")' in written_content
assert "loop()" in written_content
assert "App.setup();" in written_content
assert "App.loop();" in written_content