These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | /** |
||
4 | * Highlighter |
||
5 | * |
||
6 | * Copyright (C) 2016, Some right reserved. |
||
7 | * |
||
8 | * @author Kacper "Kadet" Donat <[email protected]> |
||
9 | * |
||
10 | * Contact with author: |
||
11 | * Xmpp: [email protected] |
||
12 | * E-mail: [email protected] |
||
13 | * |
||
14 | * From Kadet with love. |
||
15 | */ |
||
16 | |||
17 | namespace Kadet\Highlighter\bin\Commands\Benchmark; |
||
18 | |||
19 | use Kadet\Highlighter\Formatter\FormatterInterface; |
||
20 | use Kadet\Highlighter\KeyLighter; |
||
21 | use Kadet\Highlighter\Language\Language; |
||
22 | use Symfony\Component\Console\Command\Command; |
||
23 | use Symfony\Component\Console\Helper\ProgressBar; |
||
24 | use Symfony\Component\Console\Input\InputArgument; |
||
25 | use Symfony\Component\Console\Input\InputInterface; |
||
26 | use Symfony\Component\Console\Input\InputOption; |
||
27 | use Symfony\Component\Console\Output\OutputInterface; |
||
28 | |||
29 | class RunCommand extends Command |
||
30 | { |
||
31 | const DIRECTORY = "/../../../Tests/Samples/"; |
||
32 | |||
33 | protected function execute(InputInterface $input, OutputInterface $output) |
||
34 | { |
||
35 | $dir = __DIR__ . static::DIRECTORY; |
||
36 | $iterator = new \RecursiveIteratorIterator( |
||
37 | new \RecursiveDirectoryIterator( |
||
38 | $dir, |
||
39 | \RecursiveDirectoryIterator::SKIP_DOTS | \RecursiveDirectoryIterator::UNIX_PATHS |
||
40 | ), |
||
41 | \RecursiveIteratorIterator::LEAVES_ONLY |
||
42 | ); |
||
43 | |||
44 | $formatter = $input->getOption('formatter') |
||
45 | ? KeyLighter::get()->getFormatter($input->getOption('formatter')) |
||
46 | : KeyLighter::get()->getDefaultFormatter(); |
||
47 | |||
48 | $results = []; |
||
49 | |||
50 | /** @var \SplFileInfo $file */ |
||
51 | foreach ($iterator as $file) { |
||
52 | $shortname = substr($file->getPathname(), strlen($dir)); |
||
53 | |||
54 | if (!fnmatch($input->getOption('include'), $shortname)) { |
||
55 | $output->writeln(sprintf('Skipping file <info>%s</info>', $shortname), OutputInterface::VERBOSITY_VERBOSE); |
||
56 | |||
57 | continue; |
||
58 | } |
||
59 | |||
60 | $language = Language::byFilename($file->getFilename()); |
||
61 | $source = file_get_contents($file->getPathname()); |
||
62 | |||
63 | $output->writeln(sprintf( |
||
64 | 'File: <info>%s</info> Size: <info>%s</info> Language: <info>%s</info>', |
||
65 | substr($file->getPathname(), strlen($dir)), |
||
66 | $file->getSize(), |
||
67 | get_class($language) |
||
68 | ), OutputInterface::VERBOSITY_VERBOSE); |
||
69 | |||
70 | // Dry run, to include all necessary files. |
||
71 | $this->benchmark($source, $language, $formatter); |
||
72 | |||
73 | $times = []; |
||
74 | $memory = []; |
||
75 | |||
76 | for ($i = $input->getOption('times'), $progress = new ProgressBar($output, $i); $i > 0; $i--) { |
||
77 | $progress->display(); |
||
78 | $result = $this->benchmark($source, $language, $formatter); |
||
79 | $times = array_merge_recursive($times, $result['times']); |
||
80 | $memory = array_merge_recursive($memory, $result['memory']); |
||
81 | |||
82 | if ($input->getOption('geshi') && class_exists('GeSHi')) { |
||
83 | $times['geshi'][] = $this->geshi($source, $file->getExtension()); |
||
84 | } |
||
85 | |||
86 | $progress->advance(); |
||
87 | } |
||
88 | $output->write(PHP_EOL); |
||
89 | |||
90 | $results[$shortname] = [ |
||
91 | 'language' => get_class($language), |
||
92 | 'size' => $file->getSize(), |
||
93 | 'times' => $times, |
||
94 | 'memory' => $memory |
||
95 | ]; |
||
96 | } |
||
97 | |||
98 | $this->output([ |
||
99 | 'formatter' => get_class($formatter), |
||
100 | 'timestamp' => time(), |
||
101 | 'system' => php_uname(), |
||
102 | 'comment' => $input->getOption('comment'), |
||
103 | 'results' => $results |
||
104 | ], $input, $output); |
||
105 | } |
||
106 | |||
107 | protected function configure() |
||
108 | { |
||
109 | $this |
||
110 | ->setName('benchmark:run') |
||
111 | ->setDescription('Tests performance of KeyLighter') |
||
112 | ->addOption('times', 't', InputOption::VALUE_OPTIONAL, 'How many times each test will be run', 50) |
||
113 | ->addOption('include', 'i', InputOption::VALUE_OPTIONAL, 'File mask to include', '*') |
||
114 | ->addOption('comment', 'm', InputOption::VALUE_OPTIONAL, 'Comment to include in file') |
||
115 | ->addOption('pretty', 'p', InputOption::VALUE_NONE, 'Pretty print') |
||
116 | ->addOption('geshi', 'g', InputOption::VALUE_NONE, 'Include GeSHi for comparsion') |
||
117 | ->addOption('formatter', 'f', InputOption::VALUE_OPTIONAL, 'Formatter used for benchmark') |
||
118 | ->addArgument('output', InputArgument::OPTIONAL, 'Output file') |
||
119 | ; |
||
120 | } |
||
121 | |||
122 | protected function benchmark($source, Language $language, FormatterInterface $formatter, $geshi = null) |
||
0 ignored issues
–
show
|
|||
123 | { |
||
124 | gc_collect_cycles(); // force garbage collector |
||
125 | $memory = $this->getMemory(); |
||
126 | |||
127 | $tokenization = $this->_benchmark(function () use ($language, $source) { |
||
128 | return $language->tokenize($source); |
||
129 | }); |
||
130 | $tokens = $tokenization['result']; |
||
131 | |||
132 | $parsing = $this->_benchmark(function () use ($language, $tokens) { |
||
133 | return $language->parse($tokens); |
||
134 | }); |
||
135 | $parsed = $tokenization['result']; |
||
136 | |||
137 | $formatting = $this->_benchmark(function () use ($formatter, $parsed) { |
||
138 | return $formatter->format($parsed); |
||
139 | }); |
||
140 | |||
141 | return [ |
||
142 | 'times' => [ |
||
143 | 'tokenization' => $tokenization['time'], |
||
144 | 'parsing' => $parsing['time'], |
||
145 | 'formatting' => $formatting['time'], |
||
146 | 'overall' => $tokenization['time'] + $parsing['time'] + $formatting['time'] |
||
147 | ], |
||
148 | 'memory' => [ |
||
149 | 'start' => $memory, |
||
150 | 'tokenization' => $tokenization['memory'], |
||
151 | 'parsing' => $parsing['memory'], |
||
152 | 'formatting' => $formatting['memory'], |
||
153 | 'overall' => $this->getMemory() - $memory, |
||
154 | 'end' => $this->getMemory() |
||
155 | ] |
||
156 | ]; |
||
157 | } |
||
158 | |||
159 | private function _benchmark(\Closure $function) |
||
160 | { |
||
161 | $memory = $this->getMemory(); |
||
162 | $time = $this->getTime(); |
||
163 | |||
164 | $result = $function(); |
||
165 | |||
166 | return [ |
||
167 | 'result' => $result, |
||
168 | 'time' => $this->getTime() - $time, |
||
169 | 'memory' => $this->getMemory() - $memory, |
||
170 | ]; |
||
171 | } |
||
172 | |||
173 | protected function geshi($source, $language) |
||
174 | { |
||
175 | // Silence GeSHi notices |
||
176 | $previousErrorReporting = error_reporting(E_ALL ^ E_NOTICE); |
||
177 | |||
178 | $geshi = microtime(true); |
||
179 | geshi_highlight($source, $language, null, true); |
||
180 | $time = microtime(true) - $geshi; |
||
181 | |||
182 | // Restore previous error reporting level |
||
183 | error_reporting($previousErrorReporting); |
||
184 | |||
185 | return $time; |
||
186 | } |
||
187 | |||
188 | protected function output($results, InputInterface $input, OutputInterface $output) |
||
189 | { |
||
190 | /** |
||
191 | * @noinspection PhpComposerExtensionStubsInspection |
||
192 | * Adding ext-json as dev-only dependency still doesn't |
||
193 | * prevent this issue so we have to ignore this inspection |
||
194 | */ |
||
195 | $result = json_encode($results, $input->getOption('pretty') ? JSON_PRETTY_PRINT : 0); |
||
196 | |||
197 | if ($input->getArgument('output')) { |
||
198 | file_put_contents($input->getArgument('output'), $result); |
||
199 | } else { |
||
200 | $output->write($result); |
||
201 | } |
||
202 | } |
||
203 | |||
204 | protected function getMemory() |
||
205 | { |
||
206 | return memory_get_usage(); |
||
207 | } |
||
208 | |||
209 | protected function getTime() |
||
210 | { |
||
211 | return microtime(true); |
||
212 | } |
||
213 | } |
||
214 |
This check looks from parameters that have been defined for a function or method, but which are not used in the method body.