From 24741b416f0303fe7c79cb2281e4c16ab481cc2a Mon Sep 17 00:00:00 2001 From: ff225 Date: Mon, 13 Jul 2026 15:15:53 +0200 Subject: [PATCH] feat(offloading): make EMA alpha configurable instead of hard-coded 0.5 RequestHandler.handle_device_inference_result hard-coded alpha = 0.5 for the EMA smoothing of device/edge per-layer inference times. Add an optional offloading_algo.ema_alpha setting (validated in sciot.config, defaulting to 0.5) and load it via load_offloading_ema_alpha_config(). Resolves #9 --- docs/config/server.full.yaml | 6 ++++++ src/sciot/config.py | 24 +++++++++++++++++++++ src/server/communication/request_handler.py | 7 +++++- src/server/settings.yaml | 6 ++++++ tests/unit/test_config_validation.py | 18 ++++++++++++++++ tests/unit/test_request_handler_helpers.py | 13 +++++++++++ 6 files changed, 73 insertions(+), 1 deletion(-) diff --git a/docs/config/server.full.yaml b/docs/config/server.full.yaml index 3f154cc..604f536 100644 --- a/docs/config/server.full.yaml +++ b/docs/config/server.full.yaml @@ -64,6 +64,12 @@ local_inference_mode: enabled: false probability: 0.2 +# Smoothing factor for the EMA used to update per-layer device/edge inference +# times. Weight given to the newest measurement; (1 - ema_alpha) is the +# weight kept from history. Optional, defaults to 0.5. +offloading_algo: + ema_alpha: 0.5 + delay_simulation: computation: enabled: false diff --git a/src/sciot/config.py b/src/sciot/config.py index 5186cec..e347990 100644 --- a/src/sciot/config.py +++ b/src/sciot/config.py @@ -121,12 +121,36 @@ def validate_server_config(config: Mapping[str, Any]) -> dict[str, Any]: _optional_bool(normalized, "verbose", errors) _optional_bool(normalized, "debug_cprofiler", errors) _validate_evaluation_config(normalized.get("evaluation", {}), "evaluation", errors) + _validate_offloading_algo_config( + normalized.get("offloading_algo", {}), "offloading_algo", errors + ) if errors: raise ConfigValidationError(errors) return normalized +def _validate_offloading_algo_config( + value: Any, path: str, errors: list[str] +) -> None: + """Validate the optional ``offloading_algo`` section of the server config.""" + if not value: + return + if not isinstance(value, dict): + errors.append(f"{path}: must be a mapping") + return + + unexpected = set(value) - {"ema_alpha"} + if unexpected: + errors.append( + f"{path}: unsupported keys {sorted(unexpected)}; expected 'ema_alpha'" + ) + + ema_alpha = _optional_number(value, "ema_alpha", errors, path=f"{path}.ema_alpha") + if ema_alpha is not None and not (0.0 < ema_alpha <= 1.0): + errors.append(f"{path}.ema_alpha: must be between 0.0 (exclusive) and 1.0") + + def _validate_evaluation_config( value: Any, path: str, errors: list[str] ) -> None: diff --git a/src/server/communication/request_handler.py b/src/server/communication/request_handler.py index 536bad7..21a4008 100644 --- a/src/server/communication/request_handler.py +++ b/src/server/communication/request_handler.py @@ -122,6 +122,11 @@ def load_evaluation_firestore_config(): return _get_settings().get("evaluation", {}).get("firestore", {}) or {} +def load_offloading_ema_alpha_config() -> float: + """Load the EMA smoothing factor for device/edge layer inference times.""" + return float(_get_settings().get("offloading_algo", {}).get("ema_alpha", 0.5)) + + # ── Background I/O writer ─────────────────────────────────────────────────── # A single daemon thread drains a queue of callables, so that debug-JSON, # simulation-CSV, and evaluation-CSV writes never block the inference path. @@ -541,7 +546,7 @@ def handle_device_inference_result(self, body, received_timestamp): device_inference_times = RequestHandler.device_profiles[device_id]["device_inference_times"] edge_inference_times = RequestHandler.device_profiles[device_id]["edge_inference_times"] - alpha = 0.5 + alpha = load_offloading_ema_alpha_config() for l_id, inference_time in enumerate(message_data.device_layers_inference_time): layer_key = f"layer_{l_id}" if layer_key in device_inference_times: diff --git a/src/server/settings.yaml b/src/server/settings.yaml index 1625b5b..b39c168 100644 --- a/src/server/settings.yaml +++ b/src/server/settings.yaml @@ -34,6 +34,12 @@ communication: ntp_server: 0.it.pool.ntp.org port: 8080 +offloading_algo: + # Smoothing factor for the EMA used to update per-layer device/edge inference + # times (RequestHandler.handle_device_inference_result). Weight given to the + # newest measurement; (1 - ema_alpha) is the weight kept from history. + ema_alpha: 0.5 + delay_simulation: computation: enabled: false diff --git a/tests/unit/test_config_validation.py b/tests/unit/test_config_validation.py index d22a63e..bbd9651 100644 --- a/tests/unit/test_config_validation.py +++ b/tests/unit/test_config_validation.py @@ -144,6 +144,24 @@ def test_documented_example_configs_are_valid(path, loader): ), "evaluation.firestore: unsupported keys", ), + ( + lambda cfg: cfg.__setitem__( + "offloading_algo", {"ema_alpha": 0} + ), + r"offloading_algo.ema_alpha: must be between 0.0 \(exclusive\) and 1.0", + ), + ( + lambda cfg: cfg.__setitem__( + "offloading_algo", {"ema_alpha": 1.5} + ), + r"offloading_algo.ema_alpha: must be between 0.0 \(exclusive\) and 1.0", + ), + ( + lambda cfg: cfg.__setitem__( + "offloading_algo", {"unknown": True} + ), + "offloading_algo: unsupported keys", + ), ], ) def test_invalid_server_config_reports_field_errors(mutate, expected_error): diff --git a/tests/unit/test_request_handler_helpers.py b/tests/unit/test_request_handler_helpers.py index f0afee7..5d49e14 100644 --- a/tests/unit/test_request_handler_helpers.py +++ b/tests/unit/test_request_handler_helpers.py @@ -10,6 +10,7 @@ _to_float_list, load_evaluation_firestore_config, load_evaluation_outputs_config, + load_offloading_ema_alpha_config, ) @@ -87,6 +88,18 @@ def test_evaluation_firestore_config_is_loaded(monkeypatch): } +def test_offloading_ema_alpha_defaults_to_half(monkeypatch): + monkeypatch.setattr(rh, "_get_settings", lambda: {}) + assert load_offloading_ema_alpha_config() == 0.5 + + +def test_offloading_ema_alpha_is_loaded(monkeypatch): + monkeypatch.setattr( + rh, "_get_settings", lambda: {"offloading_algo": {"ema_alpha": 0.2}} + ) + assert load_offloading_ema_alpha_config() == 0.2 + + def test_append_and_publish_inference_cycle_keeps_local_write(monkeypatch): calls = []