Test Failed
Push — master ( 2bc697...ec556c )
by Maciej
02:06
created

bin/Commands/Benchmark/RunCommand.php (1 issue)

Severity

Upgrade to new PHP Analysis Engine

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
The parameter $geshi is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
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