From 3d036d941efb6d54281d04b3537d888c924dd5ee Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 23 Feb 2026 17:22:21 -0600 Subject: [PATCH] [light] Replace std::lerp with lightweight lerp_fast in LightColorValues::lerp std::lerp includes NaN/infinity edge-case handling per C++20 spec which adds ~200 bytes of overhead per call. With 10 fields interpolated, this bloated the symbol to 2,038 bytes on BK72xx. Since all light color values are pre-clamped finite floats, a simple a + t * (b - a) is sufficient. Reduces LightColorValues::lerp from 2,038 B to 286 B on BK72xx. Also saves 16 bytes on ESP8266. --- .../components/light/light_color_values.cpp | 31 ++++++++++--------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/esphome/components/light/light_color_values.cpp b/esphome/components/light/light_color_values.cpp index 2f22bb3c68..e54cc0a12a 100644 --- a/esphome/components/light/light_color_values.cpp +++ b/esphome/components/light/light_color_values.cpp @@ -1,27 +1,30 @@ #include "light_color_values.h" -#include - namespace esphome::light { +// Lightweight lerp: a + t * (b - a). +// Avoids std::lerp's NaN/infinity handling which Clang doesn't optimize out, +// adding ~200 bytes per call. Safe because all values are finite floats. +static float __attribute__((noinline)) lerp_fast(float a, float b, float t) { return a + t * (b - a); } + LightColorValues LightColorValues::lerp(const LightColorValues &start, const LightColorValues &end, float completion) { // Directly interpolate the raw values to avoid getter/setter overhead. // This is safe because: - // - All LightColorValues have their values clamped when set via the setters - // - std::lerp guarantees output is in the same range as inputs + // - All LightColorValues except color_temperature_ have their values clamped when set via the setters + // - lerp_fast output stays in range when inputs are in range and 0 <= completion <= 1 // - Therefore the output doesn't need clamping, so we can skip the setters LightColorValues v; v.color_mode_ = end.color_mode_; - v.state_ = std::lerp(start.state_, end.state_, completion); - v.brightness_ = std::lerp(start.brightness_, end.brightness_, completion); - v.color_brightness_ = std::lerp(start.color_brightness_, end.color_brightness_, completion); - v.red_ = std::lerp(start.red_, end.red_, completion); - v.green_ = std::lerp(start.green_, end.green_, completion); - v.blue_ = std::lerp(start.blue_, end.blue_, completion); - v.white_ = std::lerp(start.white_, end.white_, completion); - v.color_temperature_ = std::lerp(start.color_temperature_, end.color_temperature_, completion); - v.cold_white_ = std::lerp(start.cold_white_, end.cold_white_, completion); - v.warm_white_ = std::lerp(start.warm_white_, end.warm_white_, completion); + v.state_ = lerp_fast(start.state_, end.state_, completion); + v.brightness_ = lerp_fast(start.brightness_, end.brightness_, completion); + v.color_brightness_ = lerp_fast(start.color_brightness_, end.color_brightness_, completion); + v.red_ = lerp_fast(start.red_, end.red_, completion); + v.green_ = lerp_fast(start.green_, end.green_, completion); + v.blue_ = lerp_fast(start.blue_, end.blue_, completion); + v.white_ = lerp_fast(start.white_, end.white_, completion); + v.color_temperature_ = lerp_fast(start.color_temperature_, end.color_temperature_, completion); + v.cold_white_ = lerp_fast(start.cold_white_, end.cold_white_, completion); + v.warm_white_ = lerp_fast(start.warm_white_, end.warm_white_, completion); return v; }