[esp32] Use xTaskGetTickCount() for millis() when tick rate is 1kHz

ESPHome sets CONFIG_FREERTOS_HZ=1000, so one FreeRTOS tick equals one
millisecond and xTaskGetTickCount() returns ms directly. This replaces
esp_timer_get_time() + micros_to_millis() (a hardware timer read plus
a 64-bit multiply-shift conversion) with a single volatile memory read.

Benchmarked on real ESP32 hardware:
  Before: 686 ns/call (esp_timer_get_time + micros_to_millis)
  After:  ~20 ns/call (volatile read of xTickCount)

millis() is called 1+N times per main loop iteration (once at the top
and once per component via WarnIfComponentBlockingGuard::finish()), so
on a 5-component device this saves ~3.4 μs per loop iteration.

millis_64() is left on esp_timer_get_time() for full 64-bit μs
precision — it is only called once per loop by the Scheduler.
micros() is also unchanged (runtime_stats needs μs precision).

Falls back to the original implementation if CONFIG_FREERTOS_HZ != 1000
(non-standard user override).
This commit is contained in:
J. Nick Koston
2026-04-11 22:13:33 -10:00
parent bef4c8a86c
commit 6ac705cd66
+13 -1
View File
@@ -23,7 +23,19 @@ extern "C" __attribute__((weak)) void initArduino() {}
namespace esphome {
void HOT yield() { vPortYield(); }
uint32_t IRAM_ATTR HOT millis() { return micros_to_millis(static_cast<uint64_t>(esp_timer_get_time())); }
uint32_t IRAM_ATTR HOT millis() {
// ESPHome sets CONFIG_FREERTOS_HZ=1000 (see esp32/__init__.py), so one tick = one millisecond
// and xTaskGetTickCount() returns ms directly. This is a single volatile memory read (~20ns)
// vs esp_timer_get_time() + micros_to_millis() which does a hardware timer read + 64-bit
// multiply-shift conversion (~686ns measured). millis() is called 1+N times per main loop
// iteration (once at top + once per component for warn_blocking), so this saves ~3.4μs/loop
// on a 5-component device. micros() still uses esp_timer_get_time() for μs precision.
#if CONFIG_FREERTOS_HZ == 1000
return xTaskGetTickCount();
#else
return micros_to_millis(static_cast<uint64_t>(esp_timer_get_time()));
#endif
}
uint64_t HOT millis_64() { return micros_to_millis<uint64_t>(static_cast<uint64_t>(esp_timer_get_time())); }
void HOT delay(uint32_t ms) { vTaskDelay(ms / portTICK_PERIOD_MS); }
uint32_t IRAM_ATTR HOT micros() { return (uint32_t) esp_timer_get_time(); }