2626 * (cache primed once, then reused):
2727 *
2828 * - median wall time
29+ * - median CPU time, user + system with the workers included, when /usr/bin/time is available (Linux)
2930 * - peak resident memory, when /usr/bin/time is available (Linux)
3031 * - changed-file count AND a fingerprint of the sorted changed-file list. The fingerprint
3132 * must stay identical across refs - a perf change that alters it changed behaviour, not
@@ -95,7 +96,7 @@ public function report(array $result, ?array $baseline): void
9596 }
9697
9798 /**
98- * @return array{seconds: float, peakKb: int, changed: int, fingerprint: string}
99+ * @return array{seconds: float, cpuSeconds: float, peakKb: int, changed: int, fingerprint: string}
99100 */
100101 private function measurePhase (string $ phase , string $ configPath , string $ cacheDirectory , bool $ cold ): array
101102 {
@@ -105,6 +106,7 @@ private function measurePhase(string $phase, string $configPath, string $cacheDi
105106 }
106107
107108 $ durations = [];
109+ $ cpuDurations = [];
108110 $ peakKb = 0 ;
109111 $ changed = 0 ;
110112 $ fingerprint = '' ;
@@ -115,6 +117,7 @@ private function measurePhase(string $phase, string $configPath, string $cacheDi
115117 $ measurement = $ this ->runRector ($ configPath , $ cacheDirectory , $ cold );
116118
117119 $ durations [] = $ measurement ['seconds ' ];
120+ $ cpuDurations [] = $ measurement ['cpuSeconds ' ];
118121 $ peakKb = max ($ peakKb , $ measurement ['peakKb ' ]);
119122 $ changed = $ measurement ['changed ' ];
120123 $ fingerprint = $ measurement ['fingerprint ' ];
@@ -124,14 +127,15 @@ private function measurePhase(string $phase, string $configPath, string $cacheDi
124127
125128 return [
126129 'seconds ' => $ this ->median ($ durations ),
130+ 'cpuSeconds ' => $ this ->median ($ cpuDurations ),
127131 'peakKb ' => $ peakKb ,
128132 'changed ' => $ changed ,
129133 'fingerprint ' => $ fingerprint ,
130134 ];
131135 }
132136
133137 /**
134- * @return array{seconds: float, peakKb: int, changed: int, fingerprint: string}
138+ * @return array{seconds: float, cpuSeconds: float, peakKb: int, changed: int, fingerprint: string}
135139 */
136140 private function runRector (string $ configPath , string $ cacheDirectory , bool $ cold ): array
137141 {
@@ -159,12 +163,14 @@ private function runRector(string $configPath, string $cacheDirectory, bool $col
159163 $ seconds = (hrtime (true ) - $ startedAt ) / 1_000_000_000 ;
160164
161165 $ peakKb = $ usesGnuTime ? $ this ->parsePeakKb ($ timeOutputPath ) : 0 ;
166+ $ cpuSeconds = $ usesGnuTime ? $ this ->parseCpuSeconds ($ timeOutputPath ) : 0.0 ;
162167 @unlink ($ timeOutputPath );
163168
164169 $ changedFiles = $ this ->parseChangedFiles (is_string ($ jsonOutput ) ? $ jsonOutput : '' );
165170
166171 return [
167172 'seconds ' => $ seconds ,
173+ 'cpuSeconds ' => $ cpuSeconds ,
168174 'peakKb ' => $ peakKb ,
169175 'changed ' => count ($ changedFiles ),
170176 'fingerprint ' => substr (hash ('xxh128 ' , implode ("\n" , $ changedFiles )), 0 , 12 ),
@@ -235,6 +241,27 @@ private function parsePeakKb(string $timeErrorPath): int
235241 return 0 ;
236242 }
237243
244+ private function parseCpuSeconds (string $ timeErrorPath ): float
245+ {
246+ if (! is_file ($ timeErrorPath )) {
247+ return 0.0 ;
248+ }
249+
250+ $ contents = file_get_contents ($ timeErrorPath );
251+ if (! is_string ($ contents )) {
252+ return 0.0 ;
253+ }
254+
255+ $ cpuSeconds = 0.0 ;
256+ foreach (['User ' , 'System ' ] as $ kind ) {
257+ if (preg_match ('/ ' . $ kind . ' time \(seconds\):\s*([\d.]+)/ ' , $ contents , $ matches ) === 1 ) {
258+ $ cpuSeconds += (float ) $ matches [1 ];
259+ }
260+ }
261+
262+ return $ cpuSeconds ;
263+ }
264+
238265 private function hasGnuTime (): bool
239266 {
240267 if (! is_file ('/usr/bin/time ' )) {
@@ -266,17 +293,18 @@ private function median(array $values): float
266293 }
267294
268295 /**
269- * @param array{seconds: float, peakKb: int, changed: int, fingerprint: string} $current
270- * @param array{seconds: float, peakKb: int, changed: int, fingerprint: string}|null $baseline
296+ * @param array{seconds: float, cpuSeconds: float, peakKb: int, changed: int, fingerprint: string} $current
297+ * @param array{seconds: float, cpuSeconds: float, peakKb: int, changed: int, fingerprint: string}|null $baseline
271298 */
272299 private function printPhase (string $ phase , array $ current , ?array $ baseline ): void
273300 {
274301 printf ("## %s phase \n\n" , $ phase );
275302
276303 if ($ baseline === null ) {
277304 printf (
278- "wall: %.2f s \npeak: %s \nchanged: %d (fingerprint %s) \n\n" ,
305+ "wall: %.2f s \n cpu: %s \ npeak: %s \nchanged: %d (fingerprint %s) \n\n" ,
279306 $ current ['seconds ' ],
307+ $ this ->formatCpu ($ current ['cpuSeconds ' ]),
280308 $ this ->formatMemory ($ current ['peakKb ' ]),
281309 $ current ['changed ' ],
282310 $ current ['fingerprint ' ]
@@ -286,11 +314,13 @@ private function printPhase(string $phase, array $current, ?array $baseline): vo
286314 }
287315
288316 $ wallDelta = $ this ->percentDelta ($ baseline ['seconds ' ], $ current ['seconds ' ]);
317+ $ cpuDelta = $ this ->percentDelta ($ baseline ['cpuSeconds ' ], $ current ['cpuSeconds ' ]);
289318 $ memoryDelta = $ this ->percentDelta ((float ) $ baseline ['peakKb ' ], (float ) $ current ['peakKb ' ]);
290319
291320 printf ("| metric | baseline | this run | delta | \n" );
292321 printf ("| --- | --: | --: | --: | \n" );
293322 printf ("| wall | %.2f s | %.2f s | %s | \n" , $ baseline ['seconds ' ], $ current ['seconds ' ], $ wallDelta );
323+ printf ("| cpu | %s | %s | %s | \n" , $ this ->formatCpu ($ baseline ['cpuSeconds ' ]), $ this ->formatCpu ($ current ['cpuSeconds ' ]), $ cpuDelta );
294324 printf ("| peak | %s | %s | %s | \n" , $ this ->formatMemory ($ baseline ['peakKb ' ]), $ this ->formatMemory ($ current ['peakKb ' ]), $ memoryDelta );
295325 printf ("| changed | %d | %d | %s | \n" , $ baseline ['changed ' ], $ current ['changed ' ], $ baseline ['changed ' ] === $ current ['changed ' ] ? 'same ' : 'DIFFERENT ' );
296326
@@ -312,6 +342,15 @@ private function percentDelta(float $baseline, float $current): string
312342 return sprintf ('%+.1f%% (%s) ' , $ delta , $ delta < 0 ? 'faster/less ' : 'slower/more ' );
313343 }
314344
345+ private function formatCpu (float $ seconds ): string
346+ {
347+ if ($ seconds <= 0.0 ) {
348+ return 'n/a ' ;
349+ }
350+
351+ return sprintf ('%.2f s ' , $ seconds );
352+ }
353+
315354 private function formatMemory (int $ kb ): string
316355 {
317356 if ($ kb === 0 ) {
0 commit comments