Gemorroj » 2008.03.10 15:44

Тесты производительности различных часто употребляемых конструкций PHP кода и отдельных функций.
Для тестов использовался скрипт прилагаемый к этой статье http://wapinet.ru/textbook/speed.htm
Тестировалось на PHP 5.2.4 + Apache 2.2.4 + WinXP SP2 (Denwer 3)
Задача: просмотр содержимого папки.
Результаты

Код:

1
span style="color: #0000BB"><?php// 10000 проходов// и так 5 раз// 7.0788// 7.2023// 7.4333// 7.0996// 7.1290$dir = "./";$scan = scandir($dir);foreach($scan as $f){//print $f;}---// 7.3640// 7.4306// 7.3660// 7.4751// 7.2902$dir = "./";$open = opendir($dir);while(($f = readdir($open)) != false){//print $f;}closedir($open);---// 7.9307// 8.1111// 8.4703// 8.0594// 8.3077$dir = "./";$hand = dir($dir);while(($f = $hand->read()) != false){//print $f;}$hand->close();---// 53.0054// 53.6093// 52.9707// 53.2398// 53.3150$dir = "*";$glob = glob($dir);foreach($glob as $f){//print $f;}?>

// print $f - закомментированно для более точных результатов тестирования
glob вне конкуренции =) данная функция работает НАМНОГО медленнее остальных, тестировавшихся.
Остальные более-менее равны по производительности. Встроенный класс dir работает, как видим,  чуть медленнее, видимо сказывается ООП.

Продолжение следует...

Gemorroj » 2008.03.22 10:53

Код:

1
span style="color: #0000BB"><?php// 250000 проходов// 4.0472// 3.9551// 3.9555// 3.9487// 3.9205$test = null;if($test == null){// print $test;}// 3.9390// 3.9610// 3.9629// 3.9878// 3.9631$test = null;if($test == ''){// print $test;}// 3.9386// 3.9192// 3.9468// 3.9178// 3.9390$test = null;if($test === null){// print $test;}// 3.5278// 3.5305// 3.7416// 3.6181// 3.5312$test = null;if(!$test){// print $test;}// 7.3735// 6.6924// 6.6803// 6.7138// 6.9515$test = null;if(empty($test)){// print $test;}// 7.0340// 6.7897// 6.8383// 6.7610// 6.8296$test = null;if(isset($test)){// print $test;}?>

Интересный момент, не точная проверка на соответствие, в данном тесте, выполняется столько же, сколько и точная (== и ===).
Так же код с применением isset или empty работает заметно медленнее, это связано с тем, что вызываются функции, а в остальных случаях для проверки работают операторы сравнения, которые по определению быстрее.
Из всех приведенных примеров кода, быстрее работает код с применением отрицания (!)

Gemorroj » 2008.03.22 11:06

вот еще интересная инфа, взята из комментариев к CURL функциям на php.net =)
Calculating 50 queries to http://www.flickr.com/.
cURL took 9.550734 seconds.
file_get_contents() took 10.878360 seconds.

Calculating 50 queries to http://www.yahoo.com/.
cURL took 4.729566 seconds.
file_get_contents() took 10.443786 seconds.

Calculating 50 queries to http://www.ebay.com/.
cURL took 46.348250 seconds.
file_get_contents() took 52.685604 seconds.

Calculating 50 queries to http://www.godaddy.com/.
cURL took 1.505460 seconds.
file_get_contents() took 37.154304 seconds.

Calculating 50 queries to http://www.php.net/.
cURL took 13.136836 seconds.
file_get_contents() took 17.981879 seconds.


другими словами CURL неизбежно быстрее чем file_get_contents

Helqg » 2008.03.22 12:03

А проверка на пустоту empty()?

Gemorroj » 2008.03.22 12:49

да, точно =) не подумал, щас исправлюсь)

Helqg » 2008.03.23 09:07

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

Gemorroj » 2008.04.20 13:25

сравнивал mysql_fetch_assoc, mysql_fetch_array и mysql_fetch_row
тестировал следующим образом, 1 раз запускается этот код

Код:

1
span style="color: #0000BB"><?php// Хост, логин, пароль$con = mysql_connect('localhost','root','') or die ('Ошибка подключения к базе данных!');// Кодировкаmysql_query('SET NAMES `utf8`');// Имя БДmysql_select_db('gbs',$con);$q = mysql_query('SELECT * FROM `users`');?>

в таблице users 183 записи, состоит из 22 колонок.
далее 250000 раз запускалась одна из тестировавшихся функций.

вот что в результате имеем

Код:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$a = mysql_fetch_row($q);
4.9316
4.9642
4.9255
5.0077
4.9354
 
$a = mysql_fetch_assoc($q);
5.1141
5.0620
5.1188
5.0473
5.0907
 
$a = mysql_fetch_array($q);
5.1535
5.1544
5.2150
5.1027
5.2089

Если с mysql_fetch_row и mysql_fetch_assoc все более-менее понятно (mysql_fetch_row чуть быстрее, т.к. создает нумерованный массив, а не ассоциативный), то почему mysql_fetch_array практически не уступает mysql_fetch_assoc не совсем понятно. Ведь mysql_fetch_array создает целых 2 массива, и как мне казалось должна прилично уступать по скорости. Ан нет. Как это обьяснить без понятия.

Helqg » 2008.04.20 21:02

там написанно не 2 массива, а массив с двойными индексами. Че это такое меня спрашивать не надо.

Gemorroj » 2008.06.23 12:51

Теперь Win XP SP3 ;)
Вобщем страшное дело...

Код:

1
span style="color: #0000BB"><?php// 250000 проходовif(1)print '';/*2.81532.87822.92932.78122.81512.79462.82552.85112.82742.8898*/if(1){print '';}/*3.23253.22893.29253.23093.21443.26043.36183.24813.22013.2240*/?>

воооот...
даже не знаю что сказать( расстройство прям ='(

Helqg » 2008.06.24 10:22

для 25 тысяч не фатальна 1 секунда

Gemorroj » 2008.06.24 12:53

250 тысяч =)
но сам факт... =(

Helqg » 2008.06.24 18:18

250!!! А собственно нафига они там скобки то...

Gemorroj » 2008.06.24 19:52

ну так для меня удобнее. без скобок для меня код становится абсолютно не читабельным. да и сами разработчики PHP рекомендуют использовать скобки везде. а вот на деле со скобками медленне получается код(

Helqg » 2008.06.24 20:58

Кстати про читабельность. Ща прочитал- "Perl был основан на awk sed grep, и других средствах  UNIX , и в результате при работе с перл часто получается код только для записи( то есть через несколько месяцов вы его понять не сумеете)." А ты про скобки

Gemorroj » 2008.07.14 04:05

извечный спор, что быстрее, print или echo =)
500.000 проходов

Код:

1
span style="color: #0000BB"><?phpprint '';/*1.03411.04261.04401.03851.0431*/echo '';/*0.84390.83790.84870.83680.8507*/?>

вот такие результаты)

denich » 2008.07.14 11:09

значит echo быстрее)

AntiKiller » 2008.08.01 00:38

Я тут на сайтик наткнулся... Gemorroj, глянь... http://www.phpbench.com , если с инглишом дружишь(я думаю что дружишь ;) ), почитай там... если не дружишь(или кто ещё глянет сайтик не знающий инглиша, перезагрузи страницу которая откроется пару раз...)..........

Gemorroj » 2008.08.01 12:10

Ага, здорово. Особенно поразило на сколько быстрее код если подсчет кол-ва итераций вынести из цикла. А так же использование функций для работы с массивами array_keys() / array_values()

Gemorroj » 2009.02.28 14:20

Кто-то просил потестить скорость работы операторов AND, &&, OR, ||.
Результаты приводить не буду, т.к. разницы в скорости вообще не заметил. 100.000 проходов с любым из этих операторов занимало около 1.6 секунды.

Gemorroj » 2009.03.01 18:12

Код:

1
span style="color: #0000BB"><?php//150.000 проходовiconv_substr('1',1);//1.8843//1.8550//1.8388//1.8268//1.8288mb_substr('1',1);//1.7029//1.6956//1.6940//1.7172//1.6928substr('1',1);//1.6691//1.6661//1.6394//1.6151//1.6076?>

Как видно, шустрее всех  substr, потом mb_substr и самая медленная iconv_substr
При чем, чем входящая строка больше, тем разница ощущается сильнее.
Вообще не совсем понятно, помему библиотеки (iconv и mbstring) имеющие по большей части одинаковое предназначение разделены на 2 библиотеки.

LONGMAN » 2009.03.10 02:08

Интересно было бы сравнить ещё и while, do while, for и foreach

Gemorroj » 2009.03.10 02:22

по ссылке в первом посте достаточно много информации на этот счет. http://wapinet.ru/textbook/speed.htm думаю повторяться не стоит.

LONGMAN » 2009.03.10 02:49

Да я знаю эти тесты от Бородина, но интересно будет ещё тесты именно на Windows XP SP3

Gemorroj » 2009.04.10 19:43

Теперь PHP 5.3

файла не существует
error_reporting(0);

file_get_contents("xxx");
16.7541
17.4581
17.4037
14.7361
14.7268

@file_get_contents("xxx");
17.8958
16.7447
16.2829
14.8839
14.8261

а теперь ставим error_reporting(2039);
50.000 проходов

file_get_contents("xxx");
294.0389

@file_get_contents("xxx");
7.6748

такое получается из-за забивания буфера ошибками.

JInn » 2009.04.12 17:17

Я где то читал что при использовании знака @ код работает медленне в 7 раз... Врут?...

Gemorroj » 2009.04.12 17:40

я тоже такое читал, поэтому и решил проверить. В случае с file_get_contents выходит врут.

JInn » 2009.04.12 18:20

А можешь проверить с этим же значком, только истинное утверждение? Возможно в том случае, если не будет никаких ошибок, результаты будут другими?

Gemorroj » 2009.04.19 15:36

10.000 проходов

file_get_contents(__FILE__);
// 6.7937
// 6.9678
// 6.5534
// 6.8022
// 6.7107

@file_get_contents(__FILE__);
// 7.1111
// 7.2268
// 6.8919
// 7.0816
// 7.0639

да, действительно, если файл существует, то подавление ошибок с помощью собаки замедляет работу.

JInn » 2009.04.19 16:21

Примерно 0.3 секунды за 10000 проходов. Не так существенно, как говорят, но все равно прилично

Gemorroj » 2009.05.03 17:39

Сегодня пол дня воевал со следующим SQL запросом

Код:

1
2
3
4
5
6
7
8
9
10
SELECT DISTINCT `links`.`id` , `links`.`id_user` , `links`.`link` , `links`.`link_text` , `links`.`timeout` , `links`.`timeout_ip` , `links`.`timeout_ip_browser`
FROM `links` , `users`
WHERE `links`.`id_user` <>0
AND `links`.`16_17` = "1"
AND `links`.`sunday` = "1"
AND `links`.`in` >0
AND `links`.`on_off` = "1"
AND `links`.`comp` = "1"
AND `users`.`ban` = "0"
ORDER BY `links`.`id` DESC;

Требовалось оптимизировать... Результатов выборки без DISTINCT примерно 80.000 с DISTINCT примерно 150.
Так вот скорость мягко говоря удручает. Более 2-х секунд на средненьком сервере.
Решилось следующим образом

Код:

1
2
3
4
5
6
7
8
9
10
11
SELECT `links`.`id` , `links`.`id_user` , `links`.`link` , `links`.`link_text` , `links`.`timeout` , `links`.`timeout_ip` , `links`.`timeout_ip_browser`
FROM `links` , `users`
WHERE `links`.`id_user` <>0
AND `links`.`16_17` = "1"
AND `links`.`sunday` = "1"
AND `links`.`in` >0
AND `links`.`on_off` = "1"
AND `links`.`comp` = "1"
AND `users`.`ban` = "0"
GROUP BY `links`.`id`
ORDER BY `links`.`id` DESC;

Совсем небольшие изменения в коде, а запрос выполняется примерно за 0.2 секунды. В принципе тоже не ахти, но прогресс все равно очень заметный. В 10 раз.

JInn » 2009.05.06 03:02

Вот еще вопрос. Вывод текста с переменными. Есть три варианта.

1 - echo "text $aaa text";
2 - echo 'text '.$aaa.' text';
3 - echo "text ".$aaa." text";

В первом варианте интерпретатор должен проверять весь текст на $ и пробел, что и замедляет процесс. Это типа ищет переменную.
Во втором варианте проверка не идет (процесс быстрее), но используется конкатенация (даже две), что по идее должно замедлять процесс.
Третий вариант из области антивариантов и "Так писать не надо", так что его можно не иметь ввиду.
Собственно вопрос в том, какой из вариантов, 1 или 2, работает быстрее? Для чистоты эксперимента текст должен быть в двух вариантах. Небольшим - до 50 символов и большим - до 300 символов. Это связано с первым вариантом, там потому что поиск идет. В принципе может кто то и так знает результат?

Gemorroj » 2009.05.06 11:53

ну здесь этот вопрос рассматривался http://wapinet.ru/textbook/speed.htm
правда было это очень давно и, вероятно , на php4. Чуть позже попробую потестить на php5.3

JInn » 2009.05.06 18:47

Посмотрел... Скажем так - я в шоке... К тому, что увидел могу добавить, что
$x="test ".$test." test ".$test." test ".$test;
быстрее на 10% чем
$x="test ".$test." test ".$test." test ".$test."";
то есть если последнюю переменную не закрывать, то это хорошо. Не смог правда проверить с одинарными кавычками

Gemorroj » 2009.09.04 16:23

PHP 5.3.1
30 тысяч проходов

Код:

1
span style="color: #0000BB"><?php// 0.5504// 0.5523// 0.5499// 0.5622// 0.5541$a = 1;switch($a){ case 2: break; case 3: break; default: break;}// 0.4459// 0.4513// 0.4421// 0.4456// 0.4510$a = 1;if($a == 2){}else if($a == 3){}else{}?>

Добавлено спустя   7 минут  18 секунд:

JInn написал:

Посмотрел... Скажем так - я в шоке... К тому, что увидел могу добавить, что
$x="test ".$test." test ".$test." test ".$test;
быстрее на 10% чем
$x="test ".$test." test ".$test." test ".$test."";
то есть если последнюю переменную не закрывать, то это хорошо. Не смог правда проверить с одинарными кавычками

а что здесь шокирующего? нафига делать эту лишнюю конкатенацию с пустой строкой?

Akdmeh » 2009.09.05 11:50

Интересно, но как по мне, ради этого не стоит избегать switch, ведь там возможно без break; подключить сразу несколько условий, да и визуально кода немного меньше)

Gemorroj » 2009.09.05 11:57

Akdmeh, да, зачастую switch удобнее. Но если скорость критически важный параметр, то приходится жертвовать удобством. Я на этот switch обратил внимание когда профилировал Gmanager. Оказалось что одна совсем ничем не примечательная функция определения расширения файла, занимает приличную долю ремени. Тем более что вызывается несколько раз в цикле. Пришлось отказаться от switch и переписать на if / elseif / else. Немного, но прирост производительности это дало.

Akdmeh » 2009.09.05 13:52

Конечно, в циклах это бывает важно.
Кст, сравни у себя mb_substr и iconv_substr - интересно что у тебя выйдет.

Gemorroj » 2009.09.05 14:06

ок =)
Кстати, сейчас искал самый быстрый способ прохода по папке, без подпапок. Вероятно это достигается следующим способом:

Код:

1
span style="color: #0000BB"><?php$arr = array_diff(scandir('directory/', 0), array('.', '..'));?>

Пробовал в т.ч. новые итераторы, но с ними как минимум в 3 раза медленнее.

Gemorroj » 2009.09.05 14:17

iconv_substr быстрее примерно на треть.
100000 проходов

Код:

1
span style="color: #0000BB"><?php// 3.5349// 3.6812// 3.6436$a = "";iconv_substr($a, 0, 6, "UTF-8");// 5.7895// 5.8432// 5.8222$a = "";mb_substr($a, 0, 6, "UTF-8");?>

Такая же ситуация, если переменной $a присвоить какую-нибудь длинную строку.
Нужно отметить, что многое зависит от кодировки. Если у mb_substr стоит какая-нибудь однобайтовая кодировка, а у iconv_substr мультибайтовая, то естественно mb_substr выиграет.

AND » 2009.11.06 22:31

MySQLi. Запрос с подготовленным выражением и без него.

Код:

1
span style="color: #0000BB"><?php$db = new Mysqli('localhost', 'root', '', 'test') or die('Can not connect to MySQL');$prepare = true;$integer = 1234567890;$string = 'dgjmnvyuoplhgdenjhtrmnfqskytkgoyhjkdfmdigjewruytgrehguhksjgijerpoiwhgjeijgewyewyhlnjkbgsyigbvydbwg';$start = microtime(true); if($prepare) { $stmt = $db->prepare('INSERT INTO test (`integer`, `string`) VALUES (?, ?)'); $stmt->bind_param('is', $integer, $string); for($i = 0; $i < 10000; $i++) { $stmt->execute(); } $stmt->close(); // 10000 проходов // 5.50247 // 5.46265 // 5.59152 // 1 проход // 0.00141 // 0.00125 // 0.00172}else { for($i = 0; $i < 10000; $i++) { $db->query('INSERT INTO test (`integer`, `string`) VALUES ('.intval($integer).', "'.$db->real_escape_string($string).'")'); } // 10000 проходов // 6.11369 // 6.08014 // 6.16883 // 1 проход // 0.00082 // 0.00106 // 0.00067}echo round(microtime(true) - $start, 5);$db->close();?>
AND » 2009.11.12 16:53

PHP: красота кода сказывается на производительности
PHP: красота кода сказывается на производительности: часть 2
Интересные статьи :)

Gemorroj » 2009.11.12 19:51

действительно. спасибо.