Сравнение производительности 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++ расширение.
Обращает на себя внимание разница в потреблении памяти. Причём, если расширение выгрузить, то потребление памяти нативным кодом не уменьшается.