8000 [Routing] Match 77.7x faster by compiling routes in one regexp by nicolas-grekas · Pull Request #26059 · symfony/symfony · GitHub
[go: up one dir, main page]

Skip to content

[Routing] Match 77.7x faster by compiling routes in one regexp #26059

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 12, 2018

Conversation

nicolas-grekas
Copy link
Member
@nicolas-grekas nicolas-grekas commented Feb 5, 2018
Q A
Branch? master
Bug fix? no
New feature? no
BC breaks? no
Deprecations? no
Tests pass? yes
Fixed tickets -
License MIT
Doc PR -

Builds on top of #25961 and http://nikic.github.io/2014/02/18/Fast-request-routing-using-regular-expressions.html to make routing 77.7x faster (measured when matching the last dynamic route of 400 static + 400 dynamic routes.)

More benchs welcomed.

  • check static routes first in a switch
  • group same-modifiers regexps
  • put condition-less routes in a static map, in a switch's "default"
  • match host+pathinfo at the same time
  • group same-host regexps in sub-patterns
  • consider the host to move more static routes in the static switch
  • move static single-route "case" to "default" by adding requirements to $routes
  • group same-static-prefix in sub-patterns
  • group consecutive same-regex in a single "case"
  • move dynamic single-route "case" to "default" by adding requirements to $routes
  • extract host variables from the main match and remove the current 2nd match
  • extend same-prefix grouping to placeholders
  • group same-suffix hosts together

Here is my benchmarking code:

<?php

namespace Symfony\Component\Routing;

require 'vendor/autoload.php';

$routes = new RouteCollection();

for ($i = 0; $i < 400; ++$i) {
    $routes->add('r'.$i, new Route('/abc'.$i));
    $routes->add('f'.$i, new Route('/abc{foo}/'.$i));
}

$dumper = new Matcher\Dumper\PhpMatcherDumper($routes);

eval('?'.'>'.$dumper->dump());

$router = new \ProjectUrlMatcher(new RequestContext());

$i = 10000;
$s = microtime(1);

while (--$i) {
    $router->match('/abcdef/399');
}

echo 'Symfony: ', 1000 * (microtime(1) - $s), "\n";

namespace FastRoute;

$dispatcher = simpleDispatcher(function(RouteCollector $r) {
    for ($i = 0; $i < 400; ++$i) {
        $r->addRoute('GET', '/abc'.$i, 'r'.$i);
        $r->addRoute('GET', '/abc{foo}/'.$i, 'f'.$i);
    }
});

$i = 10000;
$s = microtime(1);

while (--$i) {
    $dispatcher->dispatch('GET', '/abcdef/399');
}

echo 'FastRoute: ', 1000 * (microtime(1) - $s), "\n";

$code .= sprintf(" case %s:\n", self::export($url));
foreach ($routes as $name => list($hasTrailingSlash, $route)) {
$methods = $route->getMethods();
$supportsTrailingSlash = $supportsRedirections && (!$methods || in_array('HEAD', $methods) || in_array('GET', $methods));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Route methods must be uppercase strings, \in_array calls could be made strict

Copy link
Member Author
@nicolas-grekas nicolas-grekas Feb 5, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is borrowed from existing code, nothing to do here: uppercase is already done by the very method you linked, and tweaking in_array() would have zero impact as this is dumping code, not critical at all

@nicolas-grekas nicolas-grekas force-pushed the router-one-rx branch 3 times, most recently from fb0de45 to 75f4efe Compare February 5, 2018 21:30
// dynamic
return $this->mergeDefaults(array_replace($matches, array('_route' => 'dynamic')), array());

break;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This break statement seems useless.


// a_second
if ('/a/22' === $pathinfo) {
break;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This break statement seems useless.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @phansys, you missed some :)
This is generated code, removing them would add a lot of complexity for no practical benefit.