Сравнение производительности

Главные вкладки

Аватар пользователя gor gor 7 февраля 2009 в 19:43

Подкинули интересные данные результатов теста
Согласно этих тестов PHP очерь долго работает.
Я повторил на своей машине тест и получил вот такие данные

 time php test.php
102334155

real    21m14.873s
user    15m48.645s
sys     4m48.129s

После у меня возникла идея переписать с ООП на функциональный подход, молучился вот такой код

<?php

function value($value){
        if($value <= 2)
            return 1;

        $f1 = value($value - 1);
        $f2 = value($value - 2);

        return $f1 + $f2;
 }
 
 $x = value (40);
 echo $x;

и вот такой вот результат теста

time php test2.php
102334155
real    2m17.596s
user    2m14.056s
sys     0m0.534s

Вот и подумалось, что отказ от ипользования ООП php в Drupal имеет однако очень даже численное подтверждение разумности данного подхода.
Функции то работают быстрее.

Комментарии

Аватар пользователя gor gor 8 февраля 2009 в 1:50

Химический Али wrote:
Интересны бы были также результаты с eAccelerator

 time php test.php
102334155

real    20m46.089s
user    15m44.416s
sys     4m48.081s
!>
 time php test2.php
102334155
real    2m22.887s
user    2m14.600s
sys     0m0.662s

тоесть практически никак, но собсно и не удивительно я этого ожидал.
От него есть существенная помошь, при "интрепетации" кода в опкод, и дальнейшее использование опкда без интрепетации.
А оптимизировать сам опкод тут как бы даже нечего - слишком простой .

Аватар пользователя perloid perloid 7 февраля 2009 в 21:09

вы наверное имели ввиду процедурный подход. пхп - не функциональный язык.
что, теперь предлагаете переписать друпал, чтоб везде
использовал рекурсию?

попробуйте еще, value(40000000);
у пхп жесткие ограничения на вложеность рекурсии.

Аватар пользователя gor gor 8 февраля 2009 в 0:44

perloid wrote:
вы наверное имели ввиду процедурный подход. пхп - не функциональный язык.
что, теперь предлагаете переписать друпал, чтоб везде
использовал рекурсию?

попробуйте еще, value(40000000);
у пхп жесткие ограничения на вложеность рекурсии.


ваш ответ совершенно не вкасу, плохое утро?

Аватар пользователя gorr gorr 8 февраля 2009 в 11:06

http://balancer.ru/tech/forum/2008/04/t61388--PHP-uskoryaem-programmy,me...
Вот еще интересная темка касательно производительности регекспов и строчных функций оттуда же, я думал, что последние быстрее, а оказывается(по наблюдениям автора поста) - наоборот! А в коде друпала очень много используется строковых функций, интересно, если их заменить на регекспы, будет ли прирост производительности и стоит ли обчинка вычинки?

Аватар пользователя bratello bratello 8 февраля 2009 в 14:04

Видимо ПХП рассматривает каждый вызов метода класса как виртуальный, и вычисляет реальное его месторасположение во время исполнения кода. Нечто подобное проходила в свое время Java, в Java Tiger джаст-ин-тайм умеет понять что у класса нет наследников и метод локейшн может вычислить всего один раз. Или что обьект создается в итерации и дальше нне используется по этому нет смысла напрягать мемори манагер и строить обьект для каждой итерации. С другой стороны, если у класса нет наследников то спрашивается зачем тогда писать такой класс, ООП кроме инкапсуляции ценен в первую очередь полиморфизмом. Вобщем тест на мой взгляд надуманный, со временем в ПХП появится какой то джаст-ин-тайм с мощными опциями оптимизации, даже если сейчас переписать задачку с которой ООП справляется на ура с использованием обычных функций - я думаю выигрыша не будет, не важно кто будет вычислять метод локейшн, в любом случае это будет происходить.

Аватар пользователя bratello bratello 15 февраля 2009 в 13:16

Переделал класс без избыточного использования памяти, производительность аналогичная производительности с применением функций:

class Fib
{
    function value($val)
    {
        if($val <= 2)
            return 1;

        $f1 = $this->value($val - 1);
        $f2 = $this->value($val - 2);

        return $f1 + $f2;
    }
}
echo date("l jS \of F Y h:i:s A");
echo "<br>";
$x = new Fib();
echo $x->value(30)."\n";
echo "<br>";
echo date("l jS \of F Y h:i:s A");
echo "<br>";

Как видим, предположения мои не подтвердились и ПХП таки умеет оптимально вычислять метод локейшн, а полученный overkill как обычно нерациональное использование памяти.

Переделал тест на call_user_func, производительность ухудшилась ~ в два раза. На мой взгляд затраты терпимые, учитывая то что количество вызовов при простроении контента страницы не такое огромное.

function value($value){
        if($value <= 2)
            return 1;
 
        $f1 = call_user_func("value",$value - 1);
        $f2 = call_user_func("value",$value - 2);
 
        return $f1 + $f2;
 }

echo date("l jS \of F Y h:i:s A");
echo "<br>";
$x = call_user_func("value", 30);
 //$x = value (30);
 echo $x;
 echo "<br>";
 echo date("l jS \of F Y h:i:s A");
echo "<br>";

Еще интересно было бы провести тесты по оптимизации строчных операций, подозреваю что оптимизация строчных операций может существенно прибавить в производительности ПХП.

Аватар пользователя gor gor 16 февраля 2009 в 17:44

bratello wrote:
Переделал класс без избыточного использования памяти, производительность аналогичная производительности с применением функций:

class Fib
{
    function value($val)
    {
        if($val <= 2)
            return 1;

        $f1 = $this->value($val - 1);
        $f2 = $this->value($val - 2);

        return $f1 + $f2;
    }
}
echo date("l jS \of F Y h:i:s A");
echo "<br>";
$x = new Fib();
echo $x->value(30)."\n";
echo "<br>";
echo date("l jS \of F Y h:i:s A");
echo "<br>";

Как видим, предположения мои не подтвердились и ПХП таки умеет оптимально вычислять метод локейшн, а полученный overkill как обычно нерациональное использование памяти.

Переделал тест на call_user_func, производительность ухудшилась ~ в два раза. На мой взгляд затраты терпимые, учитывая то что количество вызовов при простроении контента страницы не такое огромное.

function value($value){
        if($value <= 2)
            return 1;
 
        $f1 = call_user_func("value",$value - 1);
        $f2 = call_user_func("value",$value - 2);
 
        return $f1 + $f2;
 }

echo date("l jS \of F Y h:i:s A");
echo "<br>";
$x = call_user_func("value", 30);
 //$x = value (30);
 echo $x;
 echo "<br>";
 echo date("l jS \of F Y h:i:s A");
echo "<br>";

Еще интересно было бы провести тесты по оптимизации строчных операций, подозреваю что оптимизация строчных операций может существенно прибавить в производительности ПХП.


результаты тестов на сервере
Intel(R) Core(TM)2 CPU 6400 @ 2.13GHz

PHP 5.2.8 (cli) (built: Jan 21 2009 22:31:29)
Copyright (c) 1997-2008 The PHP Group
Zend Engine v2.2.0, Copyright (c) 1998-2008 Zend Technologies
with eAccelerator v0.9.5.3, Copyright (c) 2004-2006 eAccelerator, by eAccelerator
with Zend Extension Manager v1.2.2, Copyright (c) 2003-2007, by Zend Technologies
with Zend Optimizer v3.3.3, Copyright (c) 1998-2007, by Zend Technologies

1)

Monday 16th of February 2009 05:26:57 PM
832040
Monday 16th of February 2009 05:26:58 PM
real    0m1.176s
user    0m1.168s
sys     0m0.008s

2)

Monday 16th of February 2009 05:27:40 PM
832040
Monday 16th of February 2009 05:27:43 PM
real    0m2.780s
user    0m2.744s
sys     0m0.014s
Аватар пользователя bratello bratello 15 февраля 2009 в 14:17

Вот еще один интересный тест по производительности string concatenations.

function getContent(&$str, $i)
{
        $str.="$i";
        return;
}

echo date("l jS \of F Y h:i:s A");
echo "<br>";
$str = "";
for($i=0; $i < 100000; $i++)
{
        getContent(&$str, $i);
}

echo $str;
echo "<br>";
echo date("l jS \of F Y h:i:s A");
echo "<br>";

Производительность заметно ухудшится если поменять код следующим образом:

function getContent(&$str, $i)
{
        $str.="$i";
        return $str;
}

echo date("l jS \of F Y h:i:s A");
echo "<br>";
$str = "";
for($i=0; $i < 100000; $i++)
{
        $str = getContent(&$str, $i);
}

echo $str;
echo "<br>";
echo date("l jS \of F Y h:i:s A");
echo "<br>";

Другим словами, операция:

$str .= "blabla";

Гораздо дешевле (в десятки раз!) чем:

$str = $str."blabla";

В реальном ПХП приложении вторая ситуация встречается сплошь и рядом. В ПХП явно не хватает некоего класса StringBuilder, которая будет оптимизировать операции конкатенирования и копирования строк.

Например идея такая:

class StringBuilder
{
        public $buf;
}

function getContent(StringBuilder $sb, $i)
{
        $sb->buf .= "$i";
        return $sb;
}
echo date("l jS \of F Y h:i:s A");
echo "<br>";
$sb = new StringBuilder();
for($i = 0; $i< 100000; $i++)
{
        $sb = getContent($sb, $i);
}
echo $sb->buf;
echo date("l jS \of F Y h:i:s A");
echo "<br>";

Понятно что это самая общая идея, основанная на том что объекты в ПХП передаются по ссылке, а не копируются. Можно добавить методы класса для манипуляции со строками, в результате получим объектноориентированную полнофункциональную строку которая будет передаваться по ссылке и будет оптимальна.

Аватар пользователя gorr gorr 15 февраля 2009 в 14:10

bratello, спасибо, интересно, а Вы не тестировали производительность регулярных выражений против производительности строк, тоже интересно было бы посмотреть.

Аватар пользователя bratello bratello 15 февраля 2009 в 14:43

Нет, не тестировал, кажется где то были эти тесты в сети. Важно решать реальные проблемы которые прибивают производительность в большинстве случаев, провести дополнительные тесты, как я понимаю вся проблема упирается в баннальный мемори менеджмент, строки и массивы передаются в функции копированием, а не по ссылке. Достаточно будет написать оптимизированные обёртки для строк и массивов, как ситуация изменится просто драмматическим образом, друпал (и не только) большую часть времени просто тупо копирует, о кешировании можно будет забыть! Берусь написать грамотные обертки, если соберется публика - можно сделать портинг ядра друпала и может пару центральных модулей на эти обертки, как минимум темизирование и рендеринг, посмотреть результаты, с тем чтобы предложить идею на drupal.org

Аватар пользователя bratello bratello 15 февраля 2009 в 16:31

Запросто. Кстати, имплементация массивов в ПХП оказалась достаточно мощной, проделал вот такой тест:

class Form implements Iterator,  ArrayAccess
{
        protected $position = 0, $data = array();
        public function rewind() {
                $this->position = 0;
        }

        public function __construct()
        {
        }

        public function current() {
                $keys = array_keys($this->data);
                return $this->data[$keys[$this->position]];
        }

        public function key() {
                return $this->position;
        }
       
        public function next() {       
                ++$this->position;
        }
       
        public function valid() {
                return $this->position < $this->count();
        }
       
        //ArrayAccess members
        public function offsetSet($offset, $value) {
                $this->data[$offset] = $value;
        }
        public function offsetExists($offset) {
                if( $this->count() > $offset )
                        return TRUE;
                return FALSE;
        }
        public function offsetUnset($offset) {
                unset($this->data[$offset]);
        }
       
        public function offsetGet($offset) {
                if( !$this->offsetExists($offset) )
                        return NULL;
                if( count($this->data) < $offset + 1)
                        $this->fetchAllData();
                return $this->data[$offset];
        }
       
        public function count()
        {
                return count($this->data);
        }      
}

function getMyForm()
{
        //$arr = new Form();
        $arr["path1"] = array("aaa" => "bbb");
        $arr["path2"] = array("aaa" => "bbb");
        $arr["path3"] = array("aaa" => "bbb")
        $arr["path4"] = array("aaa" => "bbb");
        $arr["path5"] = array("aaa" => "bbb");
        $arr["path6"] = array("aaa" => "bbb")
        $arr["path7"] = array("aaa" => "bbb");
        $arr["path8"] = array("aaa" => "bbb");
        $arr["path9"] = array("aaa" => "bbb")
        $arr["path10"] = array("aaa" => "bbb");
        $arr["path11"] = array("aaa" => "bbb");
        $arr["path12"] = array("aaa" => "bbb");
        return $arr;
}

function renderForm($form)
{
        $form["ggg"] = "hhhh";
}

echo date("l jS \of F Y h:i:s A");
echo "<br>";

$forms = array();
for($i = 0; $i < 500; $i++)
{
        $form = getMyForm();
        for($j = 0; $j < 1000; $j++)
        {
                renderForm($form);
                renderForm($form);
                renderForm($form);
                renderForm($form);
                renderForm($form);
                renderForm($form);
        }
        $forms[] = $form;
}
echo date("l jS \of F Y h:i:s A");
echo "<br>";

Если разкоментировать первую строку в getMyForm, то получится что выигрыш минимальный, а при увеличении первой итерации и уменьшении второй итерации, массив явно выигрывает, сказывается слабая сторона аллокации объектов в ПХП. Видимо массивы реализованы хитрым способом copy on write, хотя и копирование тоже не происходит полностью. Вобщем все упирается в нормальную реализацию строковых операций, в моем тесте это разница в ~ 20 раз, даже если общий вклад строковых операций составляет одну треть, то общая производительность системы может увеличиться в два три раза. Кстати, аллокаторы памяти на разных платформах могут существенно различаться, интересно было бы все это прогнать на линухе, может оказаться что в других имплементациях ПХП на других платформах эта проблема решена. Нужно покопаться в исходниках ПХП интерпретатора.

Аватар пользователя bratello bratello 16 февраля 2009 в 16:16

Сделал дополнительные проверки со стрингами, судя по всему копирование там тоже хитрое, основная же проблема ПХП в реализации хипа памяти. В сорцах увидел реализацию ручного хипа, с кешированием, то есть сразу память не возвращается в систему, типа свой гарбич коллектор. Хип условно поделен на блоки маленького объема и блоки для большего объема, что в принципе разумно. Теперь не понятно почему пример со стринг билдером все таки работает значительно быстрее, если блок нужно с каждой итерацией увеличивать то будет периодическое реалоцирование.

class StringBuilder
{
        public $buf;
}
 
function getContent(StringBuilder $sb, $i)
{
        $sb->buf .= "$i";
        return $sb;
}
echo date("l jS \of F Y h:i:s A");
echo "<br>";
$sb = new StringBuilder();
for($i = 0; $i< 100000; $i++)
{
        $sb = getContent($sb, $i);
}
echo $sb->buf;
echo date("l jS \of F Y h:i:s A");
echo "<br>";

В интерпретаторе предусмотрен линт оптимайзер, дерево операций сводится к более простым формам, пока что не понял как его обмануть чтобы выяснить когда строка копируется реально...

Может на самом деле в ПХП есть средства, которые я ищу. К примеру, можно ли задать стратегию аллоцирования строк, сказать что строка которую я создаю имеет капасити 1к, и капасити инкремент тоже 1к, независимо от текущей длины строки, тогда количество реалокаций значительно сократится...

А у тебя на Линухе с оптимайзерами крутыми такая же ситуёвина? Может там все фикс?

Аватар пользователя bratello bratello 16 февраля 2009 в 17:36

Короче, нужно взять отладочную информацию для ПХП интерпретатора и прогнать тот же друпал на профайлере. Все эти тесты могут оказаться выгаданными проблемами, включая кстати тест с числом фибоначи. У каждого интерпретатора есть свои слабые места, но это не значит что все моторы спотыкаются именно об эти проблемные места...

Аватар пользователя bratello bratello 16 февраля 2009 в 18:25

Тесты для фибоначи(40) было бы не плохо для сравнения все таки получить... 30 я у себя в тесте на денвере использовал чтобы не выпадать по таймауту Smile Но судя по всему результат будет как в первом посте - порядка 2-х минут, колбеки будут всего в два раза хуже, что на мой взгляд терпимо.

Давай я придумаю тесты для строк, интересно было бы с оптимизатором их прогнать... Строки, в ПХП строки важнее всего, впрочем это актуально для любого веб мотора, на любой платформе.

Аватар пользователя bratello bratello 17 февраля 2009 в 16:45

Проделал профайлинг друпала, не знаю насколько можно доверять профайлеру в PhpEd, тем не менее. Оказалось строки и колбеки и близко не стоят по своим затратам производительности, основное это include_once/required_once и ДБ. Тему можно торжественно закрывать, сон разума.