Symfony — очень популярный php фреймворк, плюсы которого заключаются в прекрасном разделении кода на бандлы, DI, профилировщике, поддержке сторонних модулей. Однако же он очень медленный.
Был у меня проект на самопальном PHP фреймворке, самодельном twig-подобном шаблонизаторе без кеширования, потом он был переписан на symfony. Результаты такого преобразования оказались очень печальными: 1000 req/s превратились всего лишь в 250 запросов в секунду (production mode). Было решено выявить самые тормозные моменты этого фреймворка и попробовать ускорить его.
Итак начнем с Hello world на чистом PHP, Hello world в symfony контроллере, а также для сравнения — статика nginx, nodejs, tomcat.

Исходный код Hello world на различных языках
1) Чистый PHP
<?php
echo "Hello world";


2) Symfony вариант — контроллер
<?php

namespace AppBundle\Controller;

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;

class DefaultController extends Controller
{
    /**
     * @Route("/", name="homepage")
     */
    public function indexAction(Request $request)
    {
        echo "Hello world";
        exit;
    }
}



3) Nodejs
const http = require('http');

const hostname = '127.0.0.1';
const port = 3000;

const server = http.createServer((req, res) => {
  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain');
  res.end('Hello World\n');
});

server.listen(port, hostname, () => {
  console.log(`Hello world`);
});



# Core I3 8Gb SSD PHP7.0-fpm, nodejs6.10, tomcat8
# ab -c100 -n1000 http://localhost/hello.php, Requests per second:
1) Чистый PHP: 7216.15 [#/sec] (mean)
2) Nginx hello.txt: 14053.83 [#/sec] (mean)
3) Symfony (prod, чистый проект): 522.82 [#/sec] (mean)
4) nodejs: 3125.31 [#/sec] (mean)
5) tomcat (jsp): 15023.1 [#/sec] (mean)


Как видим, падение производительности даже в простейшем Hello world — катастрофическое (Для nodejs не выяснил в чем проблема, почему так мало, т.к. на другом сервере он выдает результаты сравнимые с nginx).

Для выяснения узких мест попробуем создать свой Kernel класс и постепенно будем добавлять к нему функциональные блоки. Для начала изменим загрузчик:
<?php
use Symfony\Component\HttpFoundation\Request;
$loader = require '.../project1/app/autoload.php';
include_once '.../project1/app/bootstrap.php.cache';
// Вместо AppKernel создаем наш переопределенный класс MyKernel
include_once '.../project1/app/MyKernel.php';
$kernel = new MyKernel('prod', false);
$kernel->loadClassCache();
$request = Request::createFromGlobals();
$response = $kernel->handle($request);
$response->send();
$kernel->terminate($request, $response); // это пока отключим


Как видим, ничего сложного тут нет, сначала инициализируется стандартный автозапуск composer, затем создается класс *Kernel, у которого вызывается несколько методов.
Далее создадим самый простейший класс, подходящий под эту схему:
Самая простая и тупая реализация MyKernel.php
<?php

use Symfony\Component\HttpKernel\Kernel;
use Symfony\Component\Config\Loader\LoaderInterface;
use Symfony\Component\HttpFoundation\Request;

class MyKernel
{
    private $dev = '';
    public function registerBundles()
    {
        $bundles = array(
            new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
            new Symfony\Bundle\SecurityBundle\SecurityBundle(),
            new Symfony\Bundle\TwigBundle\TwigBundle(),
            new Symfony\Bundle\MonologBundle\MonologBundle(),
            new Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle(),
            new Doctrine\Bundle\DoctrineBundle\DoctrineBundle(),
            new Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(),
            new AppBundle\AppBundle(),
        );
        return $bundles;
    }

    public function __construct($dev, $is)
    {
        $this->dev = $dev;
    }
    public function getEnvironment()
    {
        return $this->dev;
    }
    public function loadClassCache($name = 'classes', $extension = '.php')
    {
    }
    public function handle(Request $req, $type = Kernel::MASTER_REQUEST, $catch = true)
    {
        echo "Hello world";
        exit;
    }
    public function send()
    {
    }
    public function terminate()
    {
    }
}


В результате получаем 3894.87 [#/sec] (mean). Как видим, инициализация автозагрузки и создание нескольких классов уменьшает отзывчивость уже в 2 раза по сравнению с Hello world на чистом PHP.
На очереди загрузка app контейнера (чтобы можно было вызывать функции из services):
MyKernel.php, позволяющий работать с DI
<?php
public function handle(Request $req, $type = Kernel::MASTER_REQUEST, $catch = true)
    {
        // 1) Загрузка кешированных классов (необязательно)
        // Уменьшает rps с 3.9к до 3.3к
        require_once ".../project1/cache/prod/classes.php";
        // 2) Загрузка бандлов.
        // Этот блок кода почти не уменьшает rps, если загружен classes.php
        $bundles = $this->registerBundles();
        foreach ($bundles as $bundle) {
            $name = $bundle->getName();
            $this->bundls[$name] = $name;
            $parentName = $bundle->getParent();
        }
        // 3) Инициализация контейнера (из app-prod кеша)
        // Этот блок тоже почти не влияет на rps
        $class = "appProdProjectContainer";
        require_once ".../project1/cache/prod/appProdProjectContainer.php";
        $this->container = new $class();
        $this->container->set('kernel', $this);
        $request_stack = $this->container->get('request_stack');
        $request_stack->push($req);
        echo "Hello world";
        exit;
    }
?>


Результат уже в принципе впечатляющий — 3316.42 [#/sec] (mean)! Вы уже можете загружать сервисы из своих бандлов через $this->container->get и использовать этот Kernel например для API и REST запросов (авторизация тут конечно не работает, загрузки контроллеров пока нет).

Результат дальнейших экспериментов: удалось загрузить код контроллеров с помощью своей реализации роутинга. Возможно, если покопаться, удастся задействовать роутинг самой symfony. Twig шаблоны теоретически работают, но есть проблема с лоадером — в путях поиска не прописаны бандлы, кроме того не работают некоторые стандартные переменные, например {{ app.request }}. На данный момент эксперименты приостановлены, в проекте создан отдельный APIKernel для соответствующих api запросов, который вполне себе успешно работает.