A CI runner is shared, throttled and occasionally slow, so any assertion about wall-clock time fails eventually for reasons unrelated to the code.
// flaky, and will be deleted within a month
$start = microtime(true);
$this->service->process($batch);
$this->assertLessThan(0.5, microtime(true) - $start);
// what to assert instead: the thing that makes it fast
$this->assertLessThan(12, count(DB::getQueryLog()));
$this->assertLessThan(50 * 1024 * 1024, memory_get_peak_usage());
Query counts and memory are deterministic and are the properties that actually cause the slowness, so asserting on them catches the regression without depending on the machine. Real timing belongs in a benchmark that runs on dedicated hardware and reports a trend, which is a different tool with a different failure mode.