[core] Remove indirection from ControllerRegistry dispatch (#15173)

This commit is contained in:
J. Nick Koston
2026-03-26 15:09:16 -10:00
committed by GitHub
parent 240e53afce
commit 90e6c0d7c7
2 changed files with 13 additions and 28 deletions
+11 -11
View File
@@ -10,24 +10,24 @@ StaticVector<Controller *, CONTROLLER_REGISTRY_MAX> ControllerRegistry::controll
void ControllerRegistry::register_controller(Controller *controller) { controllers.push_back(controller); } void ControllerRegistry::register_controller(Controller *controller) { controllers.push_back(controller); }
void ControllerRegistry::notify(void *obj, DispatchFunc dispatch) { // Each notify method directly iterates controllers and calls the virtual method.
for (auto *controller : controllers) { // This avoids the overhead of a shared noinline dispatch loop with function pointer
dispatch(controller, obj); // indirection. The loop is tiny (~20 bytes per entity type) so the flash cost of
} // duplicating it is negligible compared to eliminating two levels of indirection
} // (noinline call + function pointer) from every state publish.
// Macro for standard registry notification dispatch - calls on_<entity_name>_update()
// Each wrapper passes a small trampoline lambda that calls the correct virtual method.
// NOLINTBEGIN(bugprone-macro-parentheses) // NOLINTBEGIN(bugprone-macro-parentheses)
#define CONTROLLER_REGISTRY_NOTIFY(entity_type, entity_name) \ #define CONTROLLER_REGISTRY_NOTIFY(entity_type, entity_name) \
void ControllerRegistry::notify_##entity_name##_update(entity_type *obj) { \ void ControllerRegistry::notify_##entity_name##_update(entity_type *obj) { \
notify(obj, [](Controller *c, void *o) { c->on_##entity_name##_update(static_cast<entity_type *>(o)); }); \ for (auto *controller : controllers) { \
controller->on_##entity_name##_update(obj); \
} \
} }
// Macro for entities where controller method has no "_update" suffix (Event, Update)
#define CONTROLLER_REGISTRY_NOTIFY_NO_UPDATE_SUFFIX(entity_type, entity_name) \ #define CONTROLLER_REGISTRY_NOTIFY_NO_UPDATE_SUFFIX(entity_type, entity_name) \
void ControllerRegistry::notify_##entity_name(entity_type *obj) { \ void ControllerRegistry::notify_##entity_name(entity_type *obj) { \
notify(obj, [](Controller *c, void *o) { c->on_##entity_name(static_cast<entity_type *>(o)); }); \ for (auto *controller : controllers) { \
controller->on_##entity_name(obj); \
} \
} }
// NOLINTEND(bugprone-macro-parentheses) // NOLINTEND(bugprone-macro-parentheses)
+2 -17
View File
@@ -146,8 +146,8 @@ class UpdateEntity;
* entities call ControllerRegistry::notify_*_update() which iterates the small list * entities call ControllerRegistry::notify_*_update() which iterates the small list
* of registered controllers (typically 2: API and WebServer). * of registered controllers (typically 2: API and WebServer).
* *
* Controllers read state directly from entities using existing accessors (obj->state, etc.) * Each notify method directly iterates controllers and calls the virtual method,
* rather than receiving it as callback parameters that were being ignored anyway. * avoiding function pointer indirection for minimal dispatch overhead.
* *
* Memory savings: 32 bytes per entity (2 controllers × 16 bytes std::function overhead) * Memory savings: 32 bytes per entity (2 controllers × 16 bytes std::function overhead)
* Typical config (25 entities): ~780 bytes saved * Typical config (25 entities): ~780 bytes saved
@@ -247,21 +247,6 @@ class ControllerRegistry {
#endif #endif
protected: protected:
/** Type-erased dispatch function pointer.
*
* Each notify method passes a small trampoline that calls the
* correct virtual method on Controller. The shared notify() loop
* iterates controllers once, calling the trampoline for each.
*/
using DispatchFunc = void (*)(Controller *, void *);
/** Shared dispatch loop - iterates controllers and calls dispatch for each.
*
* Marked noinline to ensure only one copy of the loop exists in flash,
* rather than being duplicated into each notify_*_update wrapper.
*/
static void __attribute__((noinline)) notify(void *obj, DispatchFunc dispatch);
static StaticVector<Controller *, CONTROLLER_REGISTRY_MAX> controllers; static StaticVector<Controller *, CONTROLLER_REGISTRY_MAX> controllers;
}; };