Completed
Push — master ( 04cf1c...b0b733 )
by Kacper
03:57
created

RunCommand::geshi()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 4
nc 1
nop 2
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 * Highlighter
4
 *
5
 * Copyright (C) 2016, Some right reserved.
6
 *
7
 * @author Kacper "Kadet" Donat <[email protected]>
8
 *
9
 * Contact with author:
10
 * Xmpp: [email protected]
11
 * E-mail: [email protected]
12
 *
13
 * From Kadet with love.
14
 */
15
16
namespace Kadet\Highlighter\bin\Commands\Benchmark;
17
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\Input\InputArgument;
24
use Symfony\Component\Console\Input\InputInterface;
25
use Symfony\Component\Console\Input\InputOption;
26
use Symfony\Component\Console\Output\OutputInterface;
27
28
class RunCommand extends Command
29
{
30
    const DIRECTORY = "../../../Tests/Samples/";
31
32
    protected function execute(InputInterface $input, OutputInterface $output)
33
    {
34
        $dir = __DIR__ . 'BenchmarkCommand.php/' .static::DIRECTORY;
35
        $iterator = new \RecursiveIteratorIterator(
36
            new \RecursiveDirectoryIterator(
37
                $dir,
38
                \RecursiveDirectoryIterator::SKIP_DOTS | \RecursiveDirectoryIterator::UNIX_PATHS
39
            ), \RecursiveIteratorIterator::LEAVES_ONLY
40
        );
41
42
        $formatter = $input->hasOption('formatter')
43
            ? KeyLighter::get()->getFormatter($input->getOption('formatter'))
44
            : KeyLighter::get()->getDefaultFormatter();
45
46
        $results = [];
47
48
        /** @var \SplFileInfo $file */
49
        foreach ($iterator as $file) {
50
            $shortname = substr($file->getPathname(), strlen($dir));
51
52
            if (!fnmatch($input->getOption('include'), $shortname)) {
53
                $output->writeln(sprintf('Skipping file <info>%s</info>', $shortname), OutputInterface::VERBOSITY_VERBOSE);
54
55
                continue;
56
            }
57
58
            $language = Language::byFilename($file->getFilename());
59
            $source = file_get_contents($file->getPathname());
60
61
            $output->writeln(sprintf(
62
                'File: <info>%s</info> Size: <info>%s</info> Language: <info>%s</info>',
63
                substr($file->getPathname(), strlen($dir)), $file->getSize(), get_class($language)
64
            ), OutputInterface::VERBOSITY_VERBOSE);
65
66
            // Dry run, to include all necessary files.
67
            $this->benchmark($source, $language, $formatter);
68
69
            $times  = [];
70
            $memory = [];
71
            for($i = $input->getOption('times'); $i > 0; $i--) {
72
                $result = $this->benchmark($source, $language, $formatter);
73
                $times  = array_merge_recursive($times, $result['times']);
74
                $memory = array_merge_recursive($memory, $result['memory']);
75
76
                if($input->getOption('geshi') && class_exists('GeSHi')) {
77
                    $times['geshi'][] = $this->geshi($source, $file->getExtension());
78
                }
79
            }
80
81
            $results[$shortname] = [
82
                'language' => get_class($language),
83
                'size'     => $file->getSize(),
84
                'times'    => $times,
85
                'memory'   => $memory
86
            ];
87
        }
88
89
        $this->output([
90
            'formatter' => get_class($formatter),
91
            'timestamp' => time(),
92
            'system'    => php_uname(),
93
            'comment'   => $input->getOption('comment'),
94
            'results'   => $results
95
        ], $input, $output);
96
    }
97
98
    protected function configure()
99
    {
100
        $this
101
            ->setName('benchmark:run')
102
            ->setDescription('Tests performance of KeyLighter')
103
            ->addOption('times', 't', InputOption::VALUE_OPTIONAL, 'How many times each test will be run', 50)
104
            ->addOption('include', 'i', InputOption::VALUE_OPTIONAL, 'File mask to include', '*')
105
            ->addOption('comment', 'm', InputOption::VALUE_OPTIONAL, 'Comment to include in file')
106
            ->addOption('pretty', 'p', InputOption::VALUE_NONE, 'Pretty print')
107
            ->addOption('geshi', 'g', InputOption::VALUE_NONE, 'Include GeSHi for comparsion')
108
            ->addOption('formatter', 'f', InputOption::VALUE_OPTIONAL, 'Formatter used for benchmark')
109
            ->addArgument('output', InputArgument::OPTIONAL, 'Output file')
110
        ;
111
    }
112
113
    protected function benchmark($source, Language $language, FormatterInterface $formatter, $geshi = null)
0 ignored issues
show
Unused Code introduced by
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...
114
    {
115
        gc_collect_cycles(); // force garbage collector
116
        $memory = $this->getMemory();
117
118
        $tokenization = $this->_benchmark(function() use ($language, $source) {
119
            return $language->tokenize($source);
120
        });
121
        $tokens = $tokenization['result'];
122
123
        $parsing = $this->_benchmark(function() use ($language, $tokens) {
124
            return $language->parse($tokens);
125
        });
126
        $parsed = $tokenization['result'];
127
128
        $formatting = $this->_benchmark(function() use ($formatter, $parsed) {
129
            return $formatter->format($parsed);
130
        });
131
132
        return [
133
            'times' => [
134
                'tokenization' => $tokenization['time'],
135
                'parsing'      => $parsing['time'],
136
                'formatting'   => $formatting['time'],
137
                'overall'      => $tokenization['time'] + $parsing['time'] + $formatting['time']
138
            ],
139
            'memory' => [
140
                'start' => $memory,
141
                'tokenization' => $tokenization['memory'],
142
                'parsing'      => $parsing['memory'],
143
                'formatting'   => $formatting['memory'],
144
                'overall' => $this->getMemory() - $memory,
145
                'end'   => $this->getMemory()
146
            ]
147
        ];
148
    }
149
150
    private function _benchmark(\Closure $function)
151
    {
152
        $memory = $this->getMemory();
153
        $time   = $this->getTime();
154
155
        $result = $function();
156
157
        return [
158
            'result' => $result,
159
            'time'   => $this->getTime() - $time,
160
            'memory' => $this->getMemory() - $memory,
161
        ];
162
    }
163
164
    protected function geshi($source, $language)
165
    {
166
        $geshi = microtime(true);
167
        geshi_highlight($source, $language, null, true);
168
        return microtime(true) - $geshi;
169
    }
170
171
    protected function output($results, InputInterface $input, OutputInterface $output)
172
    {
173
        $result = json_encode($results, $input->getOption('pretty') ? JSON_PRETTY_PRINT : 0);
174
175
        if($input->hasArgument('output')) {
176
            file_put_contents($input->getArgument('output'), $result);
177
        } else {
178
            $output->write($result);
179
        }
180
    }
181
182
    protected function getMemory()
183
    {
184
        return memory_get_usage();
185
    }
186
187
    protected function getTime()
188
    {
189
        return microtime(true);
190
    }
191
}
192