Тестирование скорости выполнения функции

В Delphi есть такой приятный оператор div - деление нацело. То есть 7 div 3 = 2, 7 mod 3 = 1. В PHP есть аналог mod - 7 % 3 = 1, а вот есть ли аналог div я постоянно забываю :)
Самое интересное, что алгоритм выяснения постоянно одинаковый. Я захожу на php.net/случайная_функция (кстати, удобнейшый вид вызова справки и самый простой способ добраться до мануала не выбирая версию online и язык), там нахожу php.net/manual/en/language.operators.arithmetic.php и ругаюсь, потому что аналога нет.
Казалось бы, Вова, что сложного написать floor($a/$b)? А вот нет, меня потянуло читать комментарии в которых часто есть хорошие советы и куски кода.

Там нашлась такая функция. Обещали, что она окажется очень быстрой

PHP:
  1. function div($x, $y) {
  2.     if ($x == 0) return 0;
  3.     if ($y == 0) return false;
  4.     return ($x - ($x % $y)) / $y;
  5. }

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

PHP:
  1. <?
  2.  
  3. // old good normal division
  4. function div1($x, $y) {
  5.     if ($x == 0) return 0;
  6.     if ($y == 0) return false;
  7.     return floor($x / $y);
  8. }
  9.  
  10. // potentially fast function
  11. function div2($x, $y) {
  12.     if ($x == 0) return 0;
  13.     if ($y == 0) return false;
  14.     return ($x - ($x % $y)) / $y;
  15. }
  16.  
  17. $timer = new Timer();
  18.  
  19. // test normal division
  20. $timer->start();
  21. for ($i=0; $i<100000000; $i++) { // yep, 100 million
  22.     $a = rand(0, 1000000);
  23.     $b = rand(0, 10000); // usually 2nd number is less than the first one
  24.     $c = div1($a, $b);
  25.     if ($i % 1000000 == 1) $timer->keepAlive();
  26. }
  27. $timer->stop();
  28. printf('Normal division: %s seconds', $timer->getTime());
  29.  
  30. echo('<br>');
  31.  
  32. // test potentially fast function
  33. $timer->start();
  34. for ($i=0; $i<100000000; $i++) {
  35.     $a = rand(0, 1000000);
  36.     $b = rand(0, 10000);
  37.     $c = div2($a, $b);
  38.     if ($i % 1000000 == 1) $timer->keepAlive();
  39. }
  40. $timer->stop();
  41. printf('New function: %s seconds', $timer->getTime());
  42.  
  43. ?>

В итоге получаем:
Normal division: 159.965 seconds
New function: 148.095 seconds

То есть, в принципе, всё равно, что использовать, огромного прироста в скорости не наблюдается. Вывод: нечего заниматься глупостями, лучше бы я настроил кеширование в текущем проекте. Так что хватит читать блоги (кроме этого :)) - пора программировать.

Но в итоге получился маленький удобный класс для тестирования всяких потенциальных полезностей. Можно запускать сразу несколько таймеров.

PHP:
  1. <?
  2.  
  3. class Timer {
  4.     private $timer = false;
  5.     private $time = false;
  6.     public $keepAliveText = '<!-- -->';
  7.  
  8.     function start() {
  9.         $this->time = false;
  10.         $this->timer = microtime(true);
  11.     }
  12.  
  13.     function stop() {
  14.         if ($this->timer==false) {
  15.             trigger_error('You should start timer first');
  16.             return false;
  17.         }
  18.  
  19.         $this->time = microtime(true) - $this->timer;
  20.         $this->timer = false;
  21.  
  22.         return $this->getTime();
  23.     }
  24.  
  25.     function getTime() {
  26.         return round($this->time * 1000)/1000;
  27.     }
  28.  
  29.     function keepAlive() {
  30.         echo($this->keepAliveText);
  31.         flush();
  32.         ob_flush();
  33.     }
  34. }
  35.  
  36. ?>


Понравилось?

  1. Подпишись через RSS
  2. Расскажи о http://php.southpark.com.ua друзьям.
    Все способы хороши: ICQ, E-mail, свой блог, комментарий в чужом блоге или сообщение на форуме
  3. Добавь статью на news2.ru, Хабрахабр или в закладки

Огромное спасибо!

И не стесняйтесь комментировать - у меня стоит плагин, который убирает rel="nofollow" у людей, которые написали больше 5 комментариев.

RSS feed | Trackback URI

8 комментариев »

Comment by Evgeny Sergeev
2007-12-06 04:12:50

Мне тоже кажется, что вот такой простой класс в тысячу раз лучше всякого рода профайлеров!
Хотя, в свое время, было много споров на тему доверия к замерам полученным твоим способом.

 
2007-12-06 11:56:38

Я обычно оптимизирую, когда чувствую, что можно ускорить как минимум в 5 раз. Поэтому точность +/- 10% меня абсолютно не волнует. Оптимизация на несколько процентов - это удел даже не больших, а огромных приложений как Facebook и Yahoo. А там используются другие способы - на чистом PHP пишутся некритичные куски, а медленные и часто используемые переписываются на каком-то более низкоуровневом языке и подключаются модулями.

 
Comment by Danaki
2008-01-23 23:51:00

Я сижу под Linux и как-то в привычку уже вошло. Делаю one-liner:
time for i in `seq 1 10`; do php script.php > /dev/null; done

Где 10 - сколько циклов, script.php - твой скрипт или с ключом -r - тоже php one-liner.
Результат че-то типа этого:
real 0m10.058s
user 0m0.000s
sys 0m0.000s

Еще в xdebug есть профайлер, все никак не соберусь сделать про него пост :D
А насчет оптимизации... этим можно заниматься вечно, поэтому стОит оптимизировать то что реально тормозит. Мы например в inbox.lv кэшируем все что можно в локальные файлы, memcache и shared memory :D

2008-01-25 10:32:31

Чувствую, я тоже пересяду на Ubuntu. Хочу многозадачности в PHP, а это работает только под Unix.

Никак, правда, не решусь, потому что люблю Office 2007 и свои утилитки на Delphi.

 
 
Comment by Аноним Subscribed to comments via email
2008-01-28 19:24:35

мда... Кто Вас учил проверять скорость выполнения функций? Вы бы еще system в цикл поставили бы.
Вам не кажется что два rand и одно условие в цикле сводят всю Вашу проверку на нет?
Попробуйте убрать оба рандома (ограничьтесь константами) и проверку. Так, чтобы в цикле было только выполнение функции div.

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

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

2008-01-28 20:22:57

Я писал, что в большинстве случаем для меня важно, когда скорость выполнения различается хотя бы в 5 раз.
Поэтому то, что вы написали, важно, но показывает невнимательность к предыдущим комментариям. :)

 
 
Comment by Аноним Subscribed to comments via email
2008-01-28 21:33:33

Проверил в Perl. Если проверять конкретно операцию целочисленного деления то div2 быстрее больше чем в 5 раз (в среднем быстрее на 650%) по сравнению с POSIX::floor. Так может Вы таки сперва проверите, а потом уже будете писать про различия в разы?

Я согласен, тестирование скорость такого деления не особо важно, потому что врятли у Вас будет столько операций деления в программе. Но тогда с другой стороны зачем тогда размещать пост? И зачем вообще изначально тестировать неправильно? Вы же не для себя пишите, так потрудитесь проработать код как следует, прежде чем его постить. ИМХО.

2008-01-28 22:40:39

Не думал, что это может ТАК сильно повлиять. Что ж, посыпаю голову пеплом.

 
 
Имя (required)
E-mail (required - never shown publicly)
URL
Текст комментария
You may use <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> in your comment.