From a54f0577c8a8a6d6c5d73fc037ec0f441d8428ad Mon Sep 17 00:00:00 2001 From: marota Date: Mon, 13 Apr 2026 23:08:11 +0200 Subject: [PATCH] Warm up the small-grid perf budget test to absorb cold-start noise The small-grid variant measured the cold first call to simulate_manual_action, while its large-grid sibling already ran a warmup iteration before timing. On a loaded CI machine the first call can spike past the 150ms budget because of module import resolution, MagicMock attribute caching and cold BLAS/pandas paths, even though the steady-state logic runs in roughly 3-10ms. Mirror the large-grid test's warmup pattern, then rebuild the env.get_obs side_effect iterator so the timed call still has a fresh pair of observations to consume. --- .../tests/test_performance_budgets.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/expert_backend/tests/test_performance_budgets.py b/expert_backend/tests/test_performance_budgets.py index cddd29f0..47810477 100644 --- a/expert_backend/tests/test_performance_budgets.py +++ b/expert_backend/tests/test_performance_budgets.py @@ -86,6 +86,23 @@ def test_simulation_logic_budget_small_grid(self, mock_get_env, mock_get_n, mock mock_get_env.return_value = env with patch.object(config, 'MONITORING_FACTOR_THERMAL_LIMITS', 0.95): + # Warm up to absorb first-call overhead (module import resolution, + # MagicMock attribute caching, cold BLAS/pandas paths). Without + # this, a cold first call on a loaded CI machine occasionally + # spikes past the budget even though the steady-state logic is + # an order of magnitude faster. Mirrors the warm-up in the + # large-grid test above. + service.simulate_manual_action("act1", "DISCO_1") + + # Rebuild the side_effect iterator — the warm-up consumed the + # first two obs values we prepared. env.get_obs is re-driven + # from this iterator on every simulate_manual_action call. + obs_n2 = self._make_large_obs(n_lines) + obs_n1_2 = self._make_large_obs(n_lines) + obs_after2 = self._make_large_obs(n_lines) + obs_n1_2.simulate.return_value = (obs_after2, None, None, {"exception": None}) + env.get_obs.side_effect = [obs_n2, obs_n1_2] + start_time = time.perf_counter() service.simulate_manual_action("act1", "DISCO_1") end_time = time.perf_counter()