Skip to content

Commit 109cddc

Browse files
committed
New Performance report to measure sniff run time performance
The report will print the sniff name, cumulative listener run time and % of the total sniff run time for each sniff triggered during a run. The report is order by cumulative listener run time in descending order. Additionally, it will highlight sniffs which have a cumulative listener run time more than twice the average run time per sniff in orange and sniffs with a cumulative listener run time of more than three times the average run time per sniff in red. At the bottom of the report it will also compare the total sniff relative run time with the total run time. Fixes 3784 Includes mention of the report in the CLI help test.
1 parent b9d7f9e commit 109cddc

File tree

3 files changed

+170
-2
lines changed

3 files changed

+170
-2
lines changed

package.xml

+1
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,7 @@ http://pear.php.net/dtd/package-2.0.xsd">
328328
<file baseinstalldir="PHP/CodeSniffer" name="Json.php" role="php" />
329329
<file baseinstalldir="PHP/CodeSniffer" name="Junit.php" role="php" />
330330
<file baseinstalldir="PHP/CodeSniffer" name="Notifysend.php" role="php" />
331+
<file baseinstalldir="PHP/CodeSniffer" name="Performance.php" role="php" />
331332
<file baseinstalldir="PHP/CodeSniffer" name="Report.php" role="php" />
332333
<file baseinstalldir="PHP/CodeSniffer" name="Source.php" role="php" />
333334
<file baseinstalldir="PHP/CodeSniffer" name="Summary.php" role="php" />

src/Config.php

+9-2
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,13 @@ public function __set($name, $value)
272272
$this->settings['trackTime'] = true;
273273
}
274274
break;
275+
case 'reports':
276+
$reports = array_change_key_case($value, CASE_LOWER);
277+
if (array_key_exists('performance', $reports) === true) {
278+
$this->settings['trackTime'] = true;
279+
}
280+
break;
281+
275282
default :
276283
// No validation required.
277284
break;
@@ -1420,8 +1427,8 @@ public function printPHPCSUsage()
14201427
echo ' <processes> How many files should be checked simultaneously (default is 1)'.PHP_EOL;
14211428
echo ' <report> Print either the "full", "xml", "checkstyle", "csv"'.PHP_EOL;
14221429
echo ' "json", "junit", "emacs", "source", "summary", "diff"'.PHP_EOL;
1423-
echo ' "svnblame", "gitblame", "hgblame" or "notifysend" report,'.PHP_EOL;
1424-
echo ' or specify the path to a custom report class'.PHP_EOL;
1430+
echo ' "svnblame", "gitblame", "hgblame", "notifysend" or "performance",'.PHP_EOL;
1431+
echo ' report or specify the path to a custom report class'.PHP_EOL;
14251432
echo ' (the "full" report is printed by default)'.PHP_EOL;
14261433
echo ' <reportFile> Write the report to the specified file path'.PHP_EOL;
14271434
echo ' <reportWidth> How many columns wide screen reports should be printed'.PHP_EOL;

src/Reports/Performance.php

+160
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
<?php
2+
/**
3+
* Performance report for PHP_CodeSniffer.
4+
*
5+
* @author Juliette Reinders Folmer <[email protected]>
6+
* @copyright 2023 Juliette Reinders Folmer. All rights reserved.
7+
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
8+
*/
9+
10+
namespace PHP_CodeSniffer\Reports;
11+
12+
use PHP_CodeSniffer\Files\File;
13+
use PHP_CodeSniffer\Util\Common;
14+
use PHP_CodeSniffer\Util\Timing;
15+
16+
class Performance implements Report
17+
{
18+
19+
20+
/**
21+
* Generate a partial report for a single processed file.
22+
*
23+
* Function should return TRUE if it printed or stored data about the file
24+
* and FALSE if it ignored the file. Returning TRUE indicates that the file and
25+
* its data should be counted in the grand totals.
26+
*
27+
* @param array $report Prepared report data.
28+
* @param \PHP_CodeSniffer\File $phpcsFile The file being reported on.
29+
* @param bool $showSources Show sources?
30+
* @param int $width Maximum allowed line width.
31+
*
32+
* @return bool
33+
*/
34+
public function generateFileReport($report, File $phpcsFile, $showSources=false, $width=80)
35+
{
36+
$times = $phpcsFile->getListenerTimes();
37+
foreach ($times as $sniff => $time) {
38+
echo "$sniff>>$time".PHP_EOL;
39+
}
40+
41+
return true;
42+
43+
}//end generateFileReport()
44+
45+
46+
/**
47+
* Prints the sniff performance report.
48+
*
49+
* @param string $cachedData Any partial report data that was returned from
50+
* generateFileReport during the run.
51+
* @param int $totalFiles Total number of files processed during the run.
52+
* @param int $totalErrors Total number of errors found during the run.
53+
* @param int $totalWarnings Total number of warnings found during the run.
54+
* @param int $totalFixable Total number of problems that can be fixed.
55+
* @param bool $showSources Show sources?
56+
* @param int $width Maximum allowed line width.
57+
* @param bool $interactive Are we running in interactive mode?
58+
* @param bool $toScreen Is the report being printed to screen?
59+
*
60+
* @return void
61+
*/
62+
public function generate(
63+
$cachedData,
64+
$totalFiles,
65+
$totalErrors,
66+
$totalWarnings,
67+
$totalFixable,
68+
$showSources=false,
69+
$width=80,
70+
$interactive=false,
71+
$toScreen=true
72+
) {
73+
$lines = explode(PHP_EOL, $cachedData);
74+
array_pop($lines);
75+
76+
if (empty($lines) === true) {
77+
return;
78+
}
79+
80+
// First collect the accumulated timings.
81+
$timings = [];
82+
$totalSniffTime = 0;
83+
foreach ($lines as $line) {
84+
$parts = explode('>>', $line);
85+
$sniffClass = $parts[0];
86+
$time = $parts[1];
87+
88+
if (isset($timings[$sniffClass]) === false) {
89+
$timings[$sniffClass] = 0;
90+
}
91+
92+
$timings[$sniffClass] += $time;
93+
$totalSniffTime += $time;
94+
}
95+
96+
// Next, tidy up the sniff names and determine max needed column width.
97+
$totalTimes = [];
98+
$maxNameWidth = 0;
99+
foreach ($timings as $sniffClass => $secs) {
100+
$sniffCode = Common::getSniffCode($sniffClass);
101+
$maxNameWidth = max($maxNameWidth, strlen($sniffCode));
102+
$totalTimes[$sniffCode] = $secs;
103+
}
104+
105+
// Leading space + up to 12 chars for the number.
106+
$maxTimeWidth = 13;
107+
// Leading space, open parenthesis, up to 5 chars for the number, space + % and close parenthesis.
108+
$maxPercWidth = 10;
109+
// Calculate the maximum width available for the sniff name.
110+
$maxNameWidth = min(($width - $maxTimeWidth - $maxPercWidth), max(($width - $maxTimeWidth - $maxPercWidth), $maxNameWidth));
111+
112+
arsort($totalTimes);
113+
114+
echo PHP_EOL."\033[1m".'PHP CODE SNIFFER SNIFF PERFORMANCE REPORT'."\033[0m".PHP_EOL;
115+
echo str_repeat('-', $width).PHP_EOL;
116+
echo "\033[1m".'SNIFF'.str_repeat(' ', ($width - 31)).'TIME TAKEN (SECS) (%)'."\033[0m".PHP_EOL;
117+
echo str_repeat('-', $width).PHP_EOL;
118+
119+
// Mark sniffs which take more than twice as long as the average processing time per sniff
120+
// in orange and when they take more than three times as long as the average,
121+
// mark them in red.
122+
$avgSniffTime = ($totalSniffTime / count($totalTimes));
123+
$doubleAvgSniffTime = (2 * $avgSniffTime);
124+
$tripleAvgSniffTime = (3 * $avgSniffTime);
125+
126+
$format = "%- {$maxNameWidth}.{$maxNameWidth}s % 12.6f (% 5.1f %%)".PHP_EOL;
127+
$formatBold = "\033[1m%- {$maxNameWidth}.{$maxNameWidth}s % 12.6f (% 5.1f %%)\033[0m".PHP_EOL;
128+
$formatWarning = "%- {$maxNameWidth}.{$maxNameWidth}s \033[33m% 12.6f (% 5.1f %%)\033[0m".PHP_EOL;
129+
$formatError = "%- {$maxNameWidth}.{$maxNameWidth}s \033[31m% 12.6f (% 5.1f %%)\033[0m".PHP_EOL;
130+
131+
foreach ($totalTimes as $sniff => $time) {
132+
$percent = round((($time / $totalSniffTime) * 100), 1);
133+
134+
if ($time > $tripleAvgSniffTime) {
135+
printf($formatError, $sniff, $time, $percent);
136+
} else if ($time > $doubleAvgSniffTime) {
137+
printf($formatWarning, $sniff, $time, $percent);
138+
} else {
139+
printf($format, $sniff, $time, $percent);
140+
}
141+
}
142+
143+
echo str_repeat('-', $width).PHP_EOL;
144+
printf($formatBold, 'TOTAL SNIFF PROCESSING TIME', $totalSniffTime, 100);
145+
146+
$runTime = (Timing::getDuration() / 1000);
147+
$phpcsTime = ($runTime - $totalSniffTime);
148+
149+
echo PHP_EOL.str_repeat('-', $width).PHP_EOL;
150+
printf($format, 'Time taken by sniffs', $totalSniffTime, round((($totalSniffTime / $runTime) * 100), 1));
151+
printf($format, 'Time taken by PHPCS runner', $phpcsTime, round((($phpcsTime / $runTime) * 100), 1));
152+
153+
echo str_repeat('-', $width).PHP_EOL;
154+
printf($formatBold, 'TOTAL RUN TIME', $runTime, 100);
155+
echo str_repeat('-', $width).PHP_EOL;
156+
157+
}//end generate()
158+
159+
160+
}//end class

0 commit comments

Comments
 (0)