123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406 |
- #!/usr/bin/env php
- <?php
- /*
- * This file is part of the league/commonmark package.
- *
- * (c) Colin O'Dell <colinodell@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
- require_once __DIR__ . '/../../vendor/autoload.php';
- use League\CommonMark\CommonMarkConverter;
- use League\CommonMark\Environment\Environment;
- use League\CommonMark\Extension\Attributes\AttributesExtension;
- use League\CommonMark\Extension\Autolink\AutolinkExtension;
- use League\CommonMark\Extension\CommonMark\CommonMarkCoreExtension;
- use League\CommonMark\Extension\DefaultAttributes\DefaultAttributesExtension;
- use League\CommonMark\Extension\DescriptionList\DescriptionListExtension;
- use League\CommonMark\Extension\DisallowedRawHtml\DisallowedRawHtmlExtension;
- use League\CommonMark\Extension\ExternalLink\ExternalLinkExtension;
- use League\CommonMark\Extension\Footnote\FootnoteExtension;
- use League\CommonMark\Extension\FrontMatter\FrontMatterExtension;
- use League\CommonMark\Extension\HeadingPermalink\HeadingPermalinkExtension;
- use League\CommonMark\Extension\Mention\MentionExtension;
- use League\CommonMark\Extension\SmartPunct\SmartPunctExtension;
- use League\CommonMark\Extension\Strikethrough\StrikethroughExtension;
- use League\CommonMark\Extension\Table\Table;
- use League\CommonMark\Extension\Table\TableExtension;
- use League\CommonMark\Extension\TableOfContents\TableOfContentsExtension;
- use League\CommonMark\Extension\TaskList\TaskListExtension;
- use League\CommonMark\GithubFlavoredMarkdownConverter;
- use League\CommonMark\MarkdownConverter;
- use Michelf\Markdown;
- use Michelf\MarkdownExtra;
- $config = [
- 'exec' => array_shift($argv),
- 'md' => sprintf('%s/sample.md', __DIR__),
- 'iterations' => 20,
- 'parser' => [],
- 'csv' => false,
- 'flags' => [
- 'isolate' => false,
- 'memory' => false,
- ],
- ];
- $usage = function (array $config, string $format, ...$args) {
- $errmsg = vsprintf("Error: {$format}", $args);
- fwrite(
- STDERR,
- <<<EOD
- {$errmsg}:
- Usage: {$config['exec']} [options] [flags]
- Options:
- --md file default: sample.md
- --iterations num default: 20
- --parser name default: all
- --csv fd|file default: disabled
- Flags:
- -isolate default: off
- -memory default: off, implies isolate
- EOD
- );
- exit(1);
- };
- while ($key = array_shift($argv)) {
- switch ($key[0]) {
- case '-': switch ($key[1]) {
- case '-':
- $key = substr($key, 2);
- if (!isset($config[$key])) {
- $usage($config, 'invalid option %s', $key);
- }
- if (is_array($config[$key])) {
- $config[$key][] = array_shift($argv);
- } else {
- $config[$key] = array_shift($argv);
- }
- break;
- default:
- $key = substr($key, 1);
- if (!isset($config['flags'][$key])) {
- $usage($config, 'invalid flag %s', $key);
- }
- $config['flags'][$key] = true;
- } break;
- default: $usage($config, $key);
- }
- }
- $config['iterations'] = max($config['iterations'], 20);
- if ($config['md'] !== '-' && !file_exists($config['md'])) {
- $usage($config, 'cannot read input %s', $config['md']);
- }
- if ($config['flags']['memory']) {
- $config['flags']['isolate'] = true;
- }
- if ($config['flags']['isolate'] && !function_exists('proc_open')) {
- $usage($config, 'isolation requires proc_open');
- }
- if ($config['csv'] !== false) {
- $stream = ctype_digit($config['csv']) ?
- "php://fd/{$config['csv']}" :
- realpath($config['csv']);
- $fd = @fopen(
- $stream,
- $config['exec'] === 'exec' ?
- 'w' : 'w+'
- );
- if (!is_resource($fd)) {
- $usage(
- $config,
- 'cannot fopen(%s) for writing',
- $config['csv']
- );
- }
- define('CSVOUT', $fd);
- if ($config['exec'] !== 'exec') {
- fputcsv(
- CSVOUT,
- ['Name', 'CPU', 'MEM']
- );
- fflush(CSVOUT);
- }
- }
- if ($config['md'] === '-') {
- $config['md'] = stream_get_contents(STDIN);
- } else {
- $config['md'] = file_get_contents($config['md']);
- }
- $parsers = [
- 'CommonMark' => function ($markdown) {
- $parser = new CommonMarkConverter();
- $parser->convert($markdown);
- },
- 'CommonMark GFM' => function ($markdown) {
- $parser = new GithubFlavoredMarkdownConverter();
- $parser->convert($markdown);
- },
- 'CommonMark All Extensions' => function ($markdown) {
- $environment = new Environment([
- 'default_attributes' => [
- Table::class => [
- 'class' => 'table',
- ],
- ],
- 'external_link' => [
- 'internal_hosts' => 'www.example.com',
- 'open_in_new_window' => true,
- 'html_class' => 'external-link',
- 'nofollow' => '',
- 'noopener' => 'external',
- 'noreferrer' => 'external',
- ],
- 'mentions' => [
- 'github_handle' => [
- 'prefix' => '@',
- 'pattern' => '[a-z\d](?:[a-z\d]|-(?=[a-z\d])){0,38}(?!\w)',
- 'generator' => 'https://github.com/%s',
- ],
- ],
- ]);
- $environment->addExtension(new CommonMarkCoreExtension());
- $environment->addExtension(new AttributesExtension());
- $environment->addExtension(new AutolinkExtension());
- $environment->addExtension(new DefaultAttributesExtension());
- $environment->addExtension(new DescriptionListExtension());
- $environment->addExtension(new DisallowedRawHtmlExtension());
- $environment->addExtension(new ExternalLinkExtension());
- $environment->addExtension(new FootnoteExtension());
- $environment->addExtension(new FrontMatterExtension());
- $environment->addExtension(new HeadingPermalinkExtension());
- $environment->addExtension(new MentionExtension());
- $environment->addExtension(new SmartPunctExtension());
- $environment->addExtension(new StrikethroughExtension());
- $environment->addExtension(new TableExtension());
- $environment->addExtension(new TableOfContentsExtension());
- $environment->addExtension(new TaskListExtension());
- $parser = new MarkdownConverter($environment);
- $parser->convert($markdown);
- },
- 'PHP Markdown' => function ($markdown) {
- Markdown::defaultTransform($markdown);
- },
- 'PHP Markdown Extra' => function ($markdown) {
- MarkdownExtra::defaultTransform($markdown);
- },
- 'Parsedown' => function ($markdown) {
- $parser = new Parsedown();
- $parser->text($markdown);
- },
- 'cebe/markdown' => function ($markdown) {
- $parser = new \cebe\markdown\Markdown();
- $parser->parse($markdown);
- },
- 'cebe/markdown gfm' => function ($markdown) {
- $parser = new \cebe\markdown\GithubMarkdown();
- $parser->parse($markdown);
- },
- 'cebe/markdown extra' => function ($markdown) {
- $parser = new \cebe\markdown\MarkdownExtra();
- $parser->parse($markdown);
- },
- ];
- if (extension_loaded('cmark')) {
- $parsers['krakjoe/cmark'] = function ($markdown) {
- \CommonMark\Render\HTML(
- \CommonMark\Parse($markdown)
- );
- };
- }
- if (count($config['parser'])) {
- $parsers = array_filter($parsers, function ($parser) use ($config) {
- return array_search($parser, $config['parser']) > -1;
- }, ARRAY_FILTER_USE_KEY);
- }
- $exec = function (array $config, string $parser) use ($parsers): array {
- $parse = $parsers[$parser];
- $start = microtime(true);
- for ($i = 0; $i < $config['iterations']; $i++) {
- echo '.';
- $parse($config['md']);
- }
- $end = microtime(true);
- $cpu = ($end - $start) * 1000;
- $cpu /= $config['iterations'];
- $cpu = round($cpu, 2);
- if ($config['flags']['memory']) {
- $mem = memory_get_peak_usage();
- $mem /= 1024;
- } else {
- $mem = 0;
- }
- if ($config['csv']) {
- fputcsv(
- CSVOUT,
- [$parser, $cpu, $mem]
- );
- fflush(CSVOUT);
- }
- return [$cpu, $mem];
- };
- if ($config['exec'] === 'exec') {
- vfprintf(
- STDERR,
- '%.2f %d',
- $exec($config, array_shift($config['parser']))
- );
- exit(0);
- }
- $run = function (array $config, string $parser) use ($exec): array {
- if ($config['flags']['isolate']) {
- $bin = str_replace(' ', '\ ', realpath($config['exec']));
- $argv =
- '--exec exec ' .
- "--parser \"{$parser}\" " .
- '--md - ' .
- "--iterations {$config['iterations']}";
- if ($config['csv']) {
- $argv .= " --csv {$config['csv']}";
- }
- if ($config['flags']['memory']) {
- $argv .= ' -memory';
- }
- $proc = proc_open("{$bin} {$argv}", [
- 0 => ['pipe', 'r'],
- 1 => STDOUT,
- 2 => ['pipe', 'w'],
- ], $pipes);
- if (is_resource($proc)) {
- fwrite($pipes[0], $config['md']);
- fclose($pipes[0]);
- $result =
- stream_get_contents($pipes[2]);
- fclose($pipes[2]);
- if (proc_close($proc) !== 0) {
- fprintf(STDERR, 'failed to close process%s', PHP_EOL);
- exit(1);
- }
- } else {
- fprintf(STDERR, 'failed to open process%s', PHP_EOL);
- exit(1);
- }
- return explode(" ", $result);
- }
- return $exec($config, $parser);
- };
- $display = function (array $config, string $title, array $fmt, array $results, string $formatName, string $formatResult): void {
- $space = $config['iterations'] - 7;
- $position = 1;
- $top = 0;
- $diff = 0;
- vprintf($title, $fmt);
- asort($results);
- foreach ($results as $name => $result) {
- if (!$top) {
- $top = $result;
- } else {
- $diff = $top - $result;
- }
- printf(
- "\t%d) $formatName $formatResult % {$space}s%s",
- $position++,
- $name,
- $result,
- $diff ?
- sprintf($formatResult, $diff) :
- null,
- PHP_EOL
- );
- }
- };
- if (extension_loaded('xdebug')) {
- fwrite(STDERR, 'The xdebug extension is loaded, this can significantly skew benchmarks. Disable it for accurate results. For xdebug 3, prefix your command with "XDEBUG_MODE=off"' . PHP_EOL . PHP_EOL);
- }
- printf(
- 'Running Benchmarks (%s), %d Implementations, %d Iterations:%s',
- $config['flags']['isolate'] ?
- 'Isolated' : 'No Isolation',
- count($parsers),
- $config['iterations'],
- PHP_EOL
- );
- $cpu = [];
- $mem = [];
- foreach ($parsers as $name => $parser) {
- printf("\t%- 30s ", $name);
- [$cpu[$name], $mem[$name]] =
- $run($config, $name);
- echo PHP_EOL;
- usleep(300000);
- }
- $display(
- $config,
- '%sBenchmark Results, CPU:%s',
- [PHP_EOL, PHP_EOL],
- $cpu,
- '%- 26s',
- '% 6.2f ms'
- );
- if (!$config['flags']['memory']) {
- exit(0);
- }
- $display(
- $config,
- '%sBenchmark Results, Peak Memory:%s',
- [PHP_EOL, PHP_EOL],
- $mem,
- '%- 26s',
- '% 6d kB'
- );
|