Сравнение производительности C++ php-расширения с нативным кодом
Давно меня интересовал вопрос: насколько увеличивается производительность при переписывании нативного кода в php-расширение.
И вот я решил провести сравнение.
В качестве платформы для написания расширения была выбрана библиотека PHP-CPP.
Пару слов о самой библиотеке PHP-CPP.
Довольно неплохая библиотека. Работать с ней приятно и удобно. Честно говоря, никогда еще не было так легко писать расширения для php.
Из минусов могу отметить недостаток документации. Чтобы разобраться с некоторыми вещами, недостаточно даже примеров, которыми автор снабжает код — приходится смотреть исходники. В частности, я так и не разобрался, как перенести статический метод класса из C++ в статический же метод в php.
Основана она на C++11, т.е. на старых дистрибутивах может потребоваться обновить gcc.
В качестве подопытного был выбран модуль постраничного разбиения.
Исходники на GitHub: https://github.com/valmat/myscrnav
Я написал два идентичных класса: один на php, другой на C++ (в виде расширения к php).
Пару слов как пользоваться.
Пример использования есть в исходниках: https://github.com/valmat/myscrnav/blob/master/screennav_test.php
Создаем pagination-объект из расширения:
$scr = new myScrNav($pageNom, $Count, '/url/to/page/');
Или из php-класса:
require 'php/class.screennav.php';
$scr = new ScreenNav($pageNom, $Count, '/url/to/page/');
Задаём необходимые параметры (если необходимо, можно не задавать):
$scr->setInterval(10);       // Сколько объектов на странице
$scr->setPrefix('?qwe&part='); // URL prefix
$scr->setPostfix('&prm=132');  // URL postfix
$scr->setSpace('<space>...</space>'); // Разделитель блоков табов
$scr->setCssName('newClassName');     // Имя класса css блока управления постраничным выводом
$scr->setMidTab(15);          // см. info.png
$scr->setMaxTab(5);           // см. info.png
$scr->showCount(true);        // Показывать ли общее количество элементов

Кроме того, доступны следующие методы для получения вычисленных данных:
getStartPos();   // Номер начального элемента на текущей странице (для выборки из БД)
getLimitPos();   // Длина списка элементов на странице на текущей странице (для выборки из БД)
getPageCnt();    // Количество страниц при разбивке на части
getStartPos();   // Номер (вычисленный) текущей страницы
show();          // Возвращает собственно сам элемент управления постраничной разбивкой (html)
То есть можно управлять постраничной разбивкой и делать запросы к БД на основе этой разбивки. Внешний вид, разумеется, полностью настраивается через css.
Теперь сами результаты сравнения.
PHP тестируется с включённым опкешером (apc). Без него смысла тестировать не вижу, ибо тестировать нужно так, как используется на рабочей системе.
1
Во-первых, сравним просто функции.
На php будет:
function ScreenNav_pageNo($var) {
    return (isset($_GET[$var])) ? ((int)$_GET[$var] - 1) : 0;
}
На C++:
Php::Value GETpageNom(Php::Parameters ¶ms) {
    if (params.size() == 0) return 0;
    string var = (new Php::Value(params[0]))->stringValue();
    string get = Php::globals["_GET"][var];
    long int rez = (new Php::Value(get))->numericValue();
    return rez ? (rez - 1) : 0;
}
php
- memory usage: 0.56Kb
 - memory peak_usage: 1.1Kb
 - Вычисленное: time: 50·10⁻⁶ sec
 - ab -n 10000 … : Time per request: 0.39 [ms] (mean)
 
C++
- memory usage: 1.26Kb
 - memory peak_usage: 1.8Kb
 - Вычисленное: time: 60·10⁻⁶ sec
 - ab -n 10000 … : Time per request: 0.4 [ms] (mean)
 
Как видно, на таком простом примере расширение не выигрывает, а даже проигрывает нативному коду.
2
Теперь сравним классы.
php
- time: 215·10⁻⁶ sec
 - memory usage: 16.5Kb
 - memory peak_usage: 22.7Kb
 - ab -n 10000 … : Time per request: 0.533 [ms] (mean)
 
C++
- time: 170·10⁻⁶ sec
 - memory usage: 2.3Kb
 - memory peak_usage: 4.6Kb
 - ab -n 10000 … : Time per request: 0.49 [ms] (mean)
 
Разница во времени порядка 10⁻⁵ sec — не то, ради чего нужно переписывать с php на C++.
С другой стороны, само то, что она проявилась на такой незначительной задаче — уже результат.
Приведённый пример позволяет понять порядок выигрыша и оценить целесообразность переписывания нативного кода в C++ расширение.
Обращает на себя внимание разница в потреблении памяти. Причём, если расширение выгрузить, то потребление памяти нативным кодом не уменьшается.