Данный пост рассматривает бенчмарк нескольких подходов к решению задачи озвученной в посте Поиск ключа по ini файлу.
Букв будет очень много. Можно сразу прокрутить до главы "Просто цифры" но тогда будет не понятно что некоторые из них означают.
Чо тестировали?
В тесте приняли участие несколько реализаций (кодовое имя: описание):
- INETCHIK_01: алгоритм предложенный пользователем INETCHIK в посте 419045
Модификация кода:выходной вариант:Code: Select all
error_reporting(E_ALL); // удалено. вряд ли кто-то будет включать вывод ошибок для небольшого куска кода. а поскольку тест запускается в консоли - там и так E_ALL по дефолту.
$ip=getenv('REMOTE_ADDR'); // удалено. айпишник приходит при вызове метода
$f='ip.txt'; // удалено. у нас свой путь к файлу
if(strpos(file_get_contents($f), $ip)===false){file_put_contents($f, "$ip=1\r\n", FILE_APPEND); } // переменная $f заменена нашей реализацией имени к файлу, последовательность \r\n на PHP_EOL, другое форматирование строк
else{
$ar=file($f); // замена $f на нашу
foreach ($ar as $k=>$v) {
if(strpos($v, $ip) !==false) {
$sub=substr($v, strlen($ip)+1);
$ar[$k]=$ip.'='.($sub+1)."\r\n"; // замена \r\n на PHP_EOL
file_put_contents($f, $ar); // замена $f на нашу
break;
}
}
}Code: Select all
class ru_php_forum_52305_INETCHIK_01 extends ru_php_forum_52305 {
public static function inc ($ip) {
if (strpos(file_get_contents(static::FILE), $ip)===false) {
file_put_contents(static::FILE, "$ip=1".PHP_EOL, FILE_APPEND);
} else {
$ar = file(static::FILE);
foreach ($ar as $k=>$v) {
if(strpos($v, $ip) !==false) {
$sub=substr($v, strlen($ip)+1);
$ar[$k]=$ip.'='.($sub+1).PHP_EOL;
file_put_contents(static::FILE, $ar);
break;
}
}
}
}
} - mahmuzar_01, mahmuzar_02, mahmuzar_03: алгоритмы пользователя mahmuzar из постов 418894 (01, 02) и 418988 (03).
Расписывать дельту не буду ибо там всего пара замен: 1) учет того что айпишник аргументом, 2) путь к файлу и 3) убрано лишнее общение скрипта с человеком.
Первая реализация первого поста:Code: Select all
<?php
class ru_php_forum_52305_mahmuzar_01 extends ru_php_forum_52305 {
public static function inc ($ip) {
/*
//открываем файл, и сохраним его содержимое в переменной
//сначала разбив на ip и его значение, которое идет после
// получим такого вида массив:
* array(3) {
[0]=>
array(2) {
[0]=>
string(12) "11.11.11.11 "
[1]=>
string(3) "3
"
}
[1]=>
array(2) {
[0]=>
string(12) "44.44.44.44 "
[1]=>
string(2) "1
"
}
[2]=>
array(2) {
[0]=>
string(12) "88.88.88.88 "
[1]=>
string(3) "1
"
}
}
*
* пример чтения файла взять из документации php.net с последюющим незначительными
* правками
* http://php.ru/manual/function.fgets.html
*/
$handle = @fopen(static::FILE, "r");//получим файловый указатель.
$i = 0;
if ($handle) {
while (($buffer = fgets($handle)) !== false) {
$ip_data[] = explode("=", $buffer);
$i++;
}
if (!feof($handle)) {
echo "Error: unexpected fgets() fail\n";
}
fclose($handle);
}
//отладка
/*echo "<pre>";
var_dump($ip_data);
echo "</pre>";*/
/*
* теперь имея массив с данными из файла, мы можем сравнивать IP-адреса с
* IP-адресом из переменной $ip
*
*
*/
//пусть $ip = "88.88.88.88";
$to_write;
for ($j = 0; $j < count($ip_data); $j++) {
if (strcmp($ip, trim($ip_data[$j][0])) === 0) {
$new_value = trim($ip_data[$j][1])+1;
$to_write .=$ip_data[$j][0] . "=" . $new_value . "\r\n";
} else {
$to_write .=$ip_data[$j][0] . "=" . trim($ip_data[$j][1]) . "\r\n";
}
}
//отладка
/*echo "<pre>";
var_dump($to_write);
echo "</pre>";*/
/*
* Теперь, сохраним изменения в файле
*
* Этот код тоже взять из документации
* с незначительными изменениями.
* http://php.ru/manual/function.fwrite.html
*/
$filename = static::FILE;
// Вначале давайте убедимся, что файл существует и доступен для записи.
if (is_writable($filename)) {
//
//Открывает файл только для записи; помещает указатель в начало файла и
//обрезает файл до нулевой длины. Если файл не существует - пробует его создать.
if (!$handle = fopen($filename, 'w')) {
echo "Не могу открыть файл ($filename)";
exit;
}
// Записываем $to_write в наш открытый файл.
if (fwrite($handle, $to_write) === FALSE) {
echo "Не могу произвести запись в файл ($filename)";
exit;
}
#echo "Ура! Записали ($somecontent) в файл ($filename)";
fclose($handle);
} else {
echo "Файл $filename недоступен для записи";
}
unset($handle);
}
}
Вторая реализация первого поста - автор добавил блокировку файла:Code: Select all
class ru_php_forum_52305_mahmuzar_02 extends ru_php_forum_52305 {
public static function inc ($ip) {
/*
//открываем файл, и сохраним его содержимое в переменной
//сначала разбив на ip и его значение, которое идет после
// получим такого вида массив:
* array(3) {
[0]=>
array(2) {
[0]=>
string(12) "11.11.11.11 "
[1]=>
string(3) "3
"
}
[1]=>
array(2) {
[0]=>
string(12) "44.44.44.44 "
[1]=>
string(2) "1
"
}
[2]=>
array(2) {
[0]=>
string(12) "88.88.88.88 "
[1]=>
string(3) "1
"
}
}
*
* пример чтения файла взять из документации php.net с последюющим незначительными
* правками
* http://php.ru/manual/function.fgets.html
*/
$handle = @fopen(static::FILE, "r"); //получим файловый указатель.
$i = 0;
if (flock($handle, LOCK_EX)) { // выполняем эксклюзивную блокировку
ftruncate($handle, 0); // очищаем файл
if ($handle) {
while (($buffer = fgets($handle)) !== false) {
$ip_data[] = explode("=", $buffer);
$i++;
}
if (!feof($handle)) {
echo "Error: unexpected fgets() fail\n";
}
fflush($handle); // очищаем вывод перед отменой блокировки
flock($handle, LOCK_UN); // отпираем файл
fclose($handle);//закрываем открыйтый дескриптор файла
}
}
//отладка
/*echo "<pre>";
var_dump($ip_data);
echo "</pre>";*/
/*
* теперь имея массив с данными из файла, мы можем сравнивать IP-адреса с
* IP-адресом из переменной $ip
*
*
*/
//пусть $ip = "88.88.88.88";
/*$ip = "88.88.88.88";*/
$to_write;
for ($j = 0; $j < count($ip_data); $j++) {
if (strcmp($ip, trim($ip_data[$j][0])) === 0) {
$new_value = trim($ip_data[$j][1]) + 1;
$to_write .=$ip_data[$j][0] . "=" . $new_value . "\r\n";
} else {
$to_write .=$ip_data[$j][0] . "=" . trim($ip_data[$j][1]) . "\r\n";
}
}
//отладка
/*echo "<pre>";
var_dump($to_write);
echo "</pre>";*/
/*
* Теперь, сохраним изменения в файле
*
* Этот код тоже взять из документации
* с незначительными изменениями.
* http://php.ru/manual/function.fwrite.html
*/
$filename = static::FILE;
// Вначале давайте убедимся, что файл существует и доступен для записи.
if (is_writable($filename)) {
//Открывает файл только для записи; помещает указатель в начало файла и
//обрезает файл до нулевой длины. Если файл не существует - пробует его создать.
if (!$handle = fopen($filename, 'w')) {
echo "Не могу открыть файл ($filename)";
exit;
}
if (flock($handle, LOCK_EX)) { // выполняем эксклюзивную блокировку
ftruncate($handle, 0); // очищаем файл
// Записываем $to_write в наш открытый файл.
if (fwrite($handle, $to_write) === FALSE) {
echo "Не могу произвести запись в файл ($filename)";
exit;
}
#echo "Ура! Записали ($somecontent) в файл ($filename)";
fflush($handle); // очищаем вывод перед отменой блокировки
flock($handle, LOCK_UN); // отпираем файл
fclose($handle);//закрываем открыйтый дескриптор файла
} else {
echo "Файл $filename недоступен для записи";
}
}
unset($handle);
}
}
Ну и третья реализация, из второго поста:Code: Select all
class ru_php_forum_52305_mahmuzar_03 extends ru_php_forum_52305 {
public static function inc ($ip) {
//откроем файл для чтения и записи
$fp = fopen(static::FILE, 'r+') or die("не удалось прочесть файл");
//пусть $ip = "132.154.226.1";
#$ip = "132.154.226.1";
if (flock($fp, LOCK_EX)) { // выполняем эксклюзивную блокировку
while (($line = fgets($fp)) !== false) {
//$line - это прочтенная строка из файла
$ip_from_file = trim(strstr($line, '=', TRUE));//получим из строки ip
$ip_value = trim(substr($line, strrpos($line, '=') + 1));//получим значение ip
if (strcmp($ip, $ip_from_file) === 0) {
$to_write .= $ip_from_file . "=" . sprintf($ip_value + 1) . "\r\n";
} else {
$to_write .=$ip_from_file . "=" . $ip_value . "\r\n";
}
}
fseek($fp, 0);//переместим указатель в начало файла
ftruncate($fp, 0); // очищаем файл
//
// Записываем $to_write в наш открытый файл.
if (fwrite($fp, $to_write) === FALSE) {
echo "Не могу произвести запись в файл";
exit;
}
fflush($fp); // очищаем вывод перед отменой блокировки
fclose($fp); //закрываем открытый дескриптор файла
}
}
} - denis01_02 - на самом деле мало отношения имеет к denis01. Бегло читая обсуждение сходил по ссылке и скопировал код, понял что его алгоритм не совсем ту задачу решает, но было интересно увидеть производительность посему решил немного переписать под себя. Если denis01 захочет - кодовое имя denis01_01 зарезервировано под его вариант.
Code: Select all
<?php
class ru_php_forum_52305_denis01_02 extends ru_php_forum_52305 {
public static function inc ($ip) {
$f = file(static::FILE, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
$out = '';
$found = false;
foreach ($f as $key => $value) {
if (!$found) {
$x = explode('=', $value);
if (trim($x[0]) == $ip) {
$x[1] = 1 + $x[1];
$found = true;
$out .= PHP_EOL . $x[0].'= '.$x[1];
continue;
}
$out .= PHP_EOL . $value;
} else {
$out .= PHP_EOL . $value;
}
}
if (!$found) {
$out .= PHP_EOL . $ip . ' = 1';
}
file_put_contents(static::FILE, ltrim($out));
}
}
На этом уходим в глубокое...
Лирическое отступление.
- Товарищь INETCHIK просил о бенчмарке алгоритма поэтому лично я не приравниваю его код к решению задачи и допускаю что код может некорректно работать с данными. На самом деле автор может спать спокойно - результаты его алгоритма близки к эталону. Ошибка мелкая, но эту ошибку допустили все участники. Даже не поверите я. Но в данном случае ошибка приводит к невосстановимой потере данных.
- Более крупная ошибка это лишние чтения. Тут уже пардоньте: как можно на бенчмарк предлагать код который ДВАЖДЫ читает файл? Это уже бутылка, разве нет?
- mahmuzar честно говоря огорчил. Все три кода высыпаются изобилием нотисов (да, не ворнингов, как я писал в основной теме), алгоритм теряет данные, алгоритм совершает лишние действия. Но автор предложил этот код именно как решение задачи. ИМХО ну нельзя предлагать что-то сырое недоделанное в данном случае. Мы говорим не о куске кода который может быть полезен в другом проекте а именно о завершенном алгоритме который должен решать сформулированную задачу.
Опять же прикрываться статусом новичка в данном случае считаю неприемлемо. Можно опубликовать с просьбой рассмотреть алгоритм опытными участниками но не постить "на отвяжитесь" (да, тут должно быть другое слово но мы не в подворотне) якобы готовое решение. Оно не готово. Банально потому что сам автор его не отлаживал.
А огорчил тем что считает это нормой. - Весь сыр-бор с бенчмарком я начал из-за моего же предложения использовать preg_replace_callback() для решения поставленной задачи. Честно я удивлен что никто не взял на себя смелость предложить рабочий алгоритм с использованием этой функции.
- А зря... Давайте подумаем над решением задачи. Её алгоритм сводится к набору следующих действий:
- Открыли файл.
- Почитали в поисках записи.
- Если нашли - инкремент счетчика.
- Если не нашли - записали строку со счетчиком равным единице.
- Сохранили файл.
Почему для этих целей нельзя использовать регулярные выражения? Мы ищем некоторую строку которая должна содержать в себе переданный айпишник, знак равенстра, сколько-то цифр которые мы за счетчик примем.
Бдыщь:Code: Select all
^IP=\d+$
Человек случайно ставит пробелы вокруг равно? Бдыщь:Code: Select all
^IP\s*=\s*\d+$
Человек уверен что вокруг равенства будет только по одному пробелу? Ну и пусть:Code: Select all
^IP = \d+$
И моё предположение было таковым: вместо того чтоб построчно итерировать содержимое файла и в каждой строке искать айпишник и делать над ним какие-то действия - быстрее будет по регулярному выражению найти вхождение айпишника и его счетчика. И в пхп-поставке регулярных выражений есть замечательная функция которая позволит сразу при нахождении вхождения айпишника соврешить инкремент его счетчика и при этом всю тягомотину с текстом "до" и с текстом "после" возьмет на себя. А если не будет вхождения то мы по традиции допишем.
И я наступил на грабли:Code: Select all
$f = file_get_contents(static::FILE);
if(false !== strstr($f, $ip.'=')) {
$f = preg_replace_callback('~^('.preg_quote($ip).')\s*=\s*(\d+)$~m', function($m) {
return $m[1] . '=' . ++$m[2];
}, $f);
} else {
$f .= PHP_EOL . $ip . '=1';
}
file_put_contents(static::FILE, $f);
Моё регулярное выражение с удовольствием находит все пробелы вокруг равно. Чего не скажешь о strstr(). Если пробелов не будет - код работает. Если будет - код просто добавляет каждый раз новую строку и всё. Это называется софтфейл. Данные не теряются. Их можно пересчитать. Но поскольку счетчики плывут - таки фейл.
Да и зачем вообще искать вхождение строки если само регулярное выражение этим занимается? Но как тогда узнать было ли вхождение? А вот так:Code: Select all
$done = false;
$f = preg_replace_callback('~^('.preg_quote($ip).')\s*=\s*(\d+)$~m', function($m) use (&$done) {
$done = true;
return $m[1] . '=' . ++$m[2];
}, file_get_contents(static::FILE));
if (!$done) {
$f .= PHP_EOL . $ip . '=1';
}
file_put_contents(static::FILE, $f);
Ну и естественно раз уж я сам упомянул состояние гонки - некоторые реализации содержат блокировку файла.
Конец лирического отступления.
Чо тестировали? Часть Ганзала.
Напомню что я-сука-такая один из своих исходников подпихнул другому автору, но честно признал подставу. Для тех кто не читал: речь о листинге denis01_02
Итак, полет моей фантазии:
- ru_php_forum_52305_preg_replace_callback_01_01: первый вариант. Пишет новые строки если есть пробелы вокруг равенства:
Code: Select all
class ru_php_forum_52305_preg_replace_callback_01_01 extends ru_php_forum_52305 {
public static function inc ($ip) {
$f = file_get_contents(static::FILE);
if(false !== strstr($f, $ip.'=')) {
$f = preg_replace_callback('~^('.preg_quote($ip).')\s*=\s*(\d+)$~m', function($m) {
return $m[1] . '=' . ++$m[2];
}, $f);
} else {
$f .= PHP_EOL . $ip . '=1';
}
file_put_contents(static::FILE, $f);
}
} - ru_php_forum_52305_preg_replace_callback_02_01: второй вариант. Красавчик вообще:
Code: Select all
class ru_php_forum_52305_preg_replace_callback_02_01 extends ru_php_forum_52305 {
public static function inc ($ip) {
$done = false;
$f = preg_replace_callback('~^('.preg_quote($ip).')\s*=\s*(\d+)$~m', function($m) use (&$done) {
$done = true;
return $m[1] . '=' . ++$m[2];
}, file_get_contents(static::FILE));
if (!$done) {
$f .= PHP_EOL . $ip . '=1';
}
file_put_contents(static::FILE, $f);
}
} - ru_php_forum_52305_preg_replace_callback_03_01: третий заход. С блокировкой файла.
Code: Select all
class ru_php_forum_52305_preg_replace_callback_03_01 extends ru_php_forum_52305 {
protected static $flock_attempts_limit = 100;
protected static $fopen_attempts_limit = 10;
public static function inc ($ip) {
$done = false;
$fopen_attempts = 0;
$flock_attempts = 0;
while (true) {
$ff = @fopen(static::FILE, 'cb+');
if (!$ff) {
fclose($ff);
if (++$fopen_attempts >= static::$fopen_attempts_limit) {
throw new Exception('FOPEN Failed');
}
usleep(100);
continue;
}
if (flock($ff, LOCK_EX|LOCK_NB)) {
break;
}
fclose($ff);
if (++$flock_attempts > static::$flock_attempts_limit) {
throw new Exception('FLOCK limit reached');
}
usleep(100);
} // while(true)
$f = preg_replace_callback('~^('.preg_quote($ip).')\s*=\s*(\d+)$~m', function($m) use (&$done) {
$done = true;
return $m[1] . '=' . ++$m[2];
}, file_get_contents(static::FILE));
if (!$done) {
$f .= PHP_EOL . $ip . '=1';
}
file_put_contents(static::FILE, $f);
fclose($ff);
}
} - ru_php_forum_52305_preg_replace_callback_03_02: третий заход, второй подзаход. Проверил что будет если мы решили что вокруг равенства будет только по одному пробелу. То есть меняется только три строки:
Code: Select all
$f = preg_replace_callback('~^('.preg_quote($ip).') = (\d+)$~m', function($m) use (&$done) { # эта
$done = true;
return $m[1] . ' = ' . ++$m[2]; # эта
}, file_get_contents(static::FILE));
if (!$done) {
$f .= PHP_EOL . $ip . ' = 1'; # и эта
}
- ru_php_forum_52305_preg_replace_callback_03_02_01: шизофрения 3-2-1. Решил проверить что будет с функцией strstr() если она тоже будет знать о фиксированном формате. Измененные строки отмечены:
Code: Select all
} // while(true)
$f = file_get_contents(static::FILE); # вот
if (false !== strstr($f, $ip.' =')) { # вот
$f = preg_replace_callback('~^('.preg_quote($ip).') = (\d+)$~m', function($m) { # вот
return $m[1] . ' = ' . ++$m[2]; # вот
}, $f); # вот
} else { # вот
$f .= PHP_EOL . $ip . ' = 1'; # вот
} # вот
file_put_contents(static::FILE, $f);
Чо тестировали? Часть Ганзала. Вне конкурса.
Естественно я заметил очевидную вещч - чем больше строк тем толще файл. Ваш КО. Но надо ли нам тратить много места на хранение айпишника? Самый длинный айпишник в в4 описывается 15 байтами текста хотя на самом деле это всего 4 байта. Поэтому я решил протестировать еще 3 подхода в которых айпишник сохраняется хексом и занимает всегда 8 байт. Добавляем одну строку упаковки айпи и в двух строках меняем название переменной $ip на $IP:
Code: Select all
} // while(true)
$IP = sprintf("%08X", ip2long($ip)); # раз
$f = preg_replace_callback('~^('.$IP.')\s*=\s*(\d+)$~m', function($m) use (&$done) { # два
$done = true;
return $m[1] . '=' . ++$m[2];
}, file_get_contents(static::FILE));
if (!$done) {
$f .= PHP_EOL . $IP . '=1'; # три
}
file_put_contents(static::FILE, $f);
Чо не тестировали?
- Не стал я писать реализацию где айпишних хранится 4 байтами и счетчик хранится тоже 4 байтами и по всему этому удобно итерируемся с офсетом в 8 байт. Хекс-реализация уже потребует конвертирования айпишника, но по крайней мере человекочитаемая база остается. А тут уже без утилит не прочитаешь.
- Не стал писать на скуэльлайте. Вроде и файл. Вроде и удобно языком структурированных запросов. Но без сторонних программ в этот файл нормально не посмотришь. Отпадает.
- Не стал рассматривать NoSQL вроде мемкэш рэдис монго и тп. У нас тут задача с примитивным файлом и кажется человек его хочет в блокноте открывать. Без лишнего апи.
Как тестировали?
Примитивно.
Создали наборы айпишников и передавали их в метод inc() каждого класса.
Каждый набор ддосил метод в несколько раундов.
Чтоб в меня не полетела стружка с жесткого диска - файл "базы" хранился на рам-диске.
Наборы адресов имеют следующие имена и свойства:
- 0: 9 зайписей для 3 уникальный айпишников скопированных из поста - 11бла 44бла и 88бла. Статистика по элементамчитается эта лабуда наверное так:
Code: Select all
e = 2 n = 2 cc = 4 cs = 4
e = 5 n = 1 cc = 5 cs = 9
по 2 вхождения, 2 адреса, суммарно вызовов 4 (дваджы-два-же-ж), контрольная сумма 4
по 5 вхождений, 1 адрес, суммарно вызовов 5, кс 9 (пять плюс четыре)
на деле речь о массиве:Code: Select all
$ips = array(
'11.11.11.11', '11.11.11.11', '11.11.11.11',
'44.44.44.44', '11.11.11.11', '88.88.88.88',
'44.44.44.44', '11.11.11.11', '88.88.88.88',
); - 1: 117 раз для всего лишь 5 айпишников. Стат
Code: Select all
e = 13 n = 1 cc = 13 cs = 13
e = 19 n = 1 cc = 19 cs = 32
e = 24 n = 1 cc = 24 cs = 56
e = 18 n = 1 cc = 18 cs = 74
e = 43 n = 1 cc = 43 cs = 117 - 2: прикол который кладет половину реализаций. 123 раза, 10 уников
Code: Select all
e = 1 n = 4 cc = 4 cs = 4
e = 2 n = 1 cc = 2 cs = 6
e = 13 n = 1 cc = 13 cs = 19
e = 19 n = 1 cc = 19 cs = 38
e = 18 n = 1 cc = 18 cs = 56
e = 24 n = 1 cc = 24 cs = 80
e = 43 n = 1 cc = 43 cs = 123
Поскольку это просто руками набитые строки плюс немного магии копи-паста - так себе данные для теста. Поэтому пишем небольшой генератор списков и рожаем себе три массива по 10000 записей с разным кол-вом уникальных и повторных записей: - g0: 217 уников размазались как
Code: Select all
e = 14 n = 11 cc = 154 cs = 154
e = 1 n = 10 cc = 10 cs = 164
e = 9 n = 8 cc = 72 cs = 236
e = 3 n = 8 cc = 24 cs = 260
e = 6 n = 7 cc = 42 cs = 302
e = 2 n = 7 cc = 14 cs = 316
e = 11 n = 6 cc = 66 cs = 382
e = 41 n = 6 cc = 246 cs = 628
e = 21 n = 6 cc = 126 cs = 754
e = 8 n = 5 cc = 40 cs = 794
e = 13 n = 5 cc = 65 cs = 859
e = 10 n = 5 cc = 50 cs = 909
e = 22 n = 5 cc = 110 cs = 1019
e = 4 n = 5 cc = 20 cs = 1039
e = 5 n = 5 cc = 25 cs = 1064
e = 23 n = 4 cc = 92 cs = 1156
e = 7 n = 4 cc = 28 cs = 1184
e = 12 n = 4 cc = 48 cs = 1232
e = 32 n = 3 cc = 96 cs = 1328
e = 15 n = 3 cc = 45 cs = 1373
e = 17 n = 3 cc = 51 cs = 1424
e = 113 n = 3 cc = 339 cs = 1763
e = 45 n = 3 cc = 135 cs = 1898
e = 16 n = 3 cc = 48 cs = 1946
e = 53 n = 3 cc = 159 cs = 2105
e = 29 n = 2 cc = 58 cs = 2163
e = 33 n = 2 cc = 66 cs = 2229
e = 27 n = 2 cc = 54 cs = 2283
e = 38 n = 2 cc = 76 cs = 2359
e = 36 n = 2 cc = 72 cs = 2431
e = 42 n = 2 cc = 84 cs = 2515
e = 57 n = 2 cc = 114 cs = 2629
e = 81 n = 2 cc = 162 cs = 2791
e = 25 n = 2 cc = 50 cs = 2841
e = 122 n = 2 cc = 244 cs = 3085
e = 56 n = 2 cc = 112 cs = 3197
e = 121 n = 2 cc = 242 cs = 3439
e = 18 n = 2 cc = 36 cs = 3475
e = 34 n = 2 cc = 68 cs = 3543
e = 35 n = 2 cc = 70 cs = 3613
e = 90 n = 2 cc = 180 cs = 3793
e = 55 n = 1 cc = 55 cs = 3848
e = 59 n = 1 cc = 59 cs = 3907
e = 37 n = 1 cc = 37 cs = 3944
e = 50 n = 1 cc = 50 cs = 3994
e = 28 n = 1 cc = 28 cs = 4022
e = 30 n = 1 cc = 30 cs = 4052
e = 39 n = 1 cc = 39 cs = 4091
e = 46 n = 1 cc = 46 cs = 4137
e = 48 n = 1 cc = 48 cs = 4185
e = 31 n = 1 cc = 31 cs = 4216
e = 26 n = 1 cc = 26 cs = 4242
e = 72 n = 1 cc = 72 cs = 4314
e = 157 n = 1 cc = 157 cs = 4471
e = 520 n = 1 cc = 520 cs = 4991
e = 153 n = 1 cc = 153 cs = 5144
e = 197 n = 1 cc = 197 cs = 5341
e = 176 n = 1 cc = 176 cs = 5517
e = 143 n = 1 cc = 143 cs = 5660
e = 165 n = 1 cc = 165 cs = 5825
e = 126 n = 1 cc = 126 cs = 5951
e = 117 n = 1 cc = 117 cs = 6068
e = 220 n = 1 cc = 220 cs = 6288
e = 265 n = 1 cc = 265 cs = 6553
e = 289 n = 1 cc = 289 cs = 6842
e = 166 n = 1 cc = 166 cs = 7008
e = 103 n = 1 cc = 103 cs = 7111
e = 251 n = 1 cc = 251 cs = 7362
e = 267 n = 1 cc = 267 cs = 7629
e = 148 n = 1 cc = 148 cs = 7777
e = 279 n = 1 cc = 279 cs = 8056
e = 129 n = 1 cc = 129 cs = 8185
e = 86 n = 1 cc = 86 cs = 8271
e = 161 n = 1 cc = 161 cs = 8432
e = 84 n = 1 cc = 84 cs = 8516
e = 79 n = 1 cc = 79 cs = 8595
e = 85 n = 1 cc = 85 cs = 8680
e = 76 n = 1 cc = 76 cs = 8756
e = 62 n = 1 cc = 62 cs = 8818
e = 51 n = 1 cc = 51 cs = 8869
e = 61 n = 1 cc = 61 cs = 8930
e = 58 n = 1 cc = 58 cs = 8988
e = 70 n = 1 cc = 70 cs = 9058
e = 91 n = 1 cc = 91 cs = 9149
e = 40 n = 1 cc = 40 cs = 9189
e = 63 n = 1 cc = 63 cs = 9252
e = 100 n = 1 cc = 100 cs = 9352
e = 114 n = 1 cc = 114 cs = 9466
e = 97 n = 1 cc = 97 cs = 9563
e = 95 n = 1 cc = 95 cs = 9658
e = 77 n = 1 cc = 77 cs = 9735
e = 105 n = 1 cc = 105 cs = 9840
e = 111 n = 1 cc = 111 cs = 9951
e = 49 n = 1 cc = 49 cs = 10000 - g1: 1488 (оно само! ранодомом! честно!) уников
Code: Select all
e = 1 n = 340 cc = 340 cs = 340
e = 2 n = 233 cc = 466 cs = 806
e = 3 n = 172 cc = 516 cs = 1322
e = 4 n = 118 cc = 472 cs = 1794
e = 6 n = 89 cc = 534 cs = 2328
e = 5 n = 86 cc = 430 cs = 2758
e = 7 n = 66 cc = 462 cs = 3220
e = 8 n = 54 cc = 432 cs = 3652
e = 9 n = 41 cc = 369 cs = 4021
e = 10 n = 34 cc = 340 cs = 4361
e = 12 n = 27 cc = 324 cs = 4685
e = 11 n = 21 cc = 231 cs = 4916
e = 13 n = 17 cc = 221 cs = 5137
e = 20 n = 17 cc = 340 cs = 5477
e = 15 n = 16 cc = 240 cs = 5717
e = 14 n = 15 cc = 210 cs = 5927
e = 17 n = 13 cc = 221 cs = 6148
e = 16 n = 12 cc = 192 cs = 6340
e = 25 n = 12 cc = 300 cs = 6640
e = 19 n = 10 cc = 190 cs = 6830
e = 18 n = 9 cc = 162 cs = 6992
e = 21 n = 8 cc = 168 cs = 7160
e = 33 n = 7 cc = 231 cs = 7391
e = 23 n = 6 cc = 138 cs = 7529
e = 29 n = 5 cc = 145 cs = 7674
e = 24 n = 5 cc = 120 cs = 7794
e = 22 n = 5 cc = 110 cs = 7904
e = 31 n = 4 cc = 124 cs = 8028
e = 39 n = 3 cc = 117 cs = 8145
e = 36 n = 3 cc = 108 cs = 8253
e = 28 n = 3 cc = 84 cs = 8337
e = 35 n = 3 cc = 105 cs = 8442
e = 27 n = 3 cc = 81 cs = 8523
e = 26 n = 2 cc = 52 cs = 8575
e = 43 n = 2 cc = 86 cs = 8661
e = 32 n = 2 cc = 64 cs = 8725
e = 34 n = 2 cc = 68 cs = 8793
e = 38 n = 2 cc = 76 cs = 8869
e = 37 n = 2 cc = 74 cs = 8943
e = 46 n = 2 cc = 92 cs = 9035
e = 50 n = 1 cc = 50 cs = 9085
e = 70 n = 1 cc = 70 cs = 9155
e = 63 n = 1 cc = 63 cs = 9218
e = 49 n = 1 cc = 49 cs = 9267
e = 78 n = 1 cc = 78 cs = 9345
e = 60 n = 1 cc = 60 cs = 9405
e = 48 n = 1 cc = 48 cs = 9453
e = 55 n = 1 cc = 55 cs = 9508
e = 72 n = 1 cc = 72 cs = 9580
e = 58 n = 1 cc = 58 cs = 9638
e = 47 n = 1 cc = 47 cs = 9685
e = 51 n = 1 cc = 51 cs = 9736
e = 41 n = 1 cc = 41 cs = 9777
e = 54 n = 1 cc = 54 cs = 9831
e = 30 n = 1 cc = 30 cs = 9861
e = 40 n = 1 cc = 40 cs = 9901
e = 99 n = 1 cc = 99 cs = 10000 - g2: 6921 уник
Code: Select all
e = 1 n = 5068 cc = 5068 cs = 5068
e = 2 n = 1186 cc = 2372 cs = 7440
e = 3 n = 373 cc = 1119 cs = 8559
e = 4 n = 163 cc = 652 cs = 9211
e = 5 n = 75 cc = 375 cs = 9586
e = 6 n = 25 cc = 150 cs = 9736
e = 7 n = 15 cc = 105 cs = 9841
e = 8 n = 8 cc = 64 cs = 9905
e = 9 n = 5 cc = 45 cs = 9950
e = 10 n = 1 cc = 10 cs = 9960
e = 16 n = 1 cc = 16 cs = 9976
e = 24 n = 1 cc = 24 cs = 10000
Предпоследнее что нужно знать перед чтением результатов это что за BASE такой. Это два режима с которым тест начинает насиловать файл.
Если перед нами INIT значит сценарий записал 3 айпишника как это предоставил автор темы.
А строка COPY значит что мы перед тестом копируем уже готовый толстый файл с 6900+ адресов и начинаем тупо накручивать у них счетчики.
Зачем?
Если изначально база пустая и в нее налетают уники - она постепенно растет. И производительность полного цикла уменьшается. Поэтому результат INIT-теста в самом начале практически как скорость фотона но постепенно замедляется. А среднюю мы считаем по всему тесту. А вот COPY практически не будет изменять размер файла. Только когда десятки-сотни-тысячи будут лишний разряд накидывать. Следовательно весь тест будет более равномерным.
После воодушевляющих результатов чужих реализаций решил COPY-тест проводить только со своими алгоритмами. Я честно запускал их но при результатах допустим в 1416 секунд на один тест - я бы всю эту херню, которую никто не прочитает, закончил бы только к апрелю. Я уж молчу про гигабайты нотисов mahmuzar-а - и так понятно что алгоритм не работает и не вижу смысла его дальше насиловать.
Ну и последнее - код класса который мы расширияем всей толпой:
Code: Select all
abstract class ru_php_forum_52305 {
const BASE = '/var/tmp/ramdrive/base.txt';
const FILE = '/var/tmp/ramdrive/ru_php_forum_52305.txt';
const DIR = '/var/tmp/ramdrive';
public static function init () {
file_put_contents(static::FILE,
'11.11.11.11 = 3' . PHP_EOL .
'44.44.44.44 = 1' . PHP_EOL .
'88.88.88.88 = 1'
);
}
abstract public static function inc ($ip);
public static function test () {
if (CFG::$alloc) {
static::init();
} else {
copy(static::BASE, static::FILE);
}
global $ips;
$begin = microtime(true);
for($i = 0; $i < CFG::$rounds; $i++) {
foreach ($ips as $ip) {
static::inc($ip);
}
}
$ela = microtime(true) - $begin;
echo sprintf("%-60s : %0.6f %0.6f %8d\n", get_called_class(), $ela, ($ela / CFG::$idone), memory_get_peak_usage()); flush();
copy(static::FILE, static::DIR.'/'.date('Y-md-His.').(++CFG::$pc).'.'.get_called_class());
}
}После последнего: bench.php
Code: Select all
class CFG {
public static
$pc = 100,
$rounds = 1,
$idone = 0,
$alloc = true
;
}
require_once 'ru_php_forum_52305.php';
$tests = array(
'ru_php_forum_52305_INETCHIK_01',
'ru_php_forum_52305_strstr_01',
'ru_php_forum_52305_preg_replace_callback_01_01',
'ru_php_forum_52305_preg_replace_callback_02_01',
'ru_php_forum_52305_preg_replace_callback_03_01',
'ru_php_forum_52305_preg_replace_callback_03_02',
'ru_php_forum_52305_preg_replace_callback_03_02_01',
'ru_php_forum_52305_mahmuzar_01',
'ru_php_forum_52305_mahmuzar_02',
'ru_php_forum_52305_mahmuzar_03',
'ru_php_forum_52305_denis01_02',
'ru_php_forum_52305_prc_03_01_hex_01',
'ru_php_forum_52305_prc_03_02_hex_01',
'ru_php_forum_52305_prc_03_02_01_hex_01',
);
$datas = array(
'0', '1', '2', 'g0', 'g1', 'g2',
);
foreach ($tests as $test) {
require_once $test . '.php';
}
// INIT-test
foreach ($datas as $d) {
require_once './ips/data_'.$d.'.php';
$ips_cnt = count($ips);
$ips_unq = count(array_unique($ips));
CFG::$idone = $ips_cnt * CFG::$rounds;
echo sprintf("\n"
. "BANK : %8s\n"
. "CNT : %8d\n"
. "UNQ : %8d\n"
. "ROUNDS : %8d\n"
. "BASE : %8s\n"
, $d, $ips_cnt, $ips_unq, CFG::$rounds, (CFG::$alloc ? 'INIT' : 'COPY')
);
foreach ($tests as $test) {
$test::test();
}
}
// COPY-test
$tests2 = array(
'ru_php_forum_52305_preg_replace_callback_03_01',
'ru_php_forum_52305_preg_replace_callback_03_02',
'ru_php_forum_52305_preg_replace_callback_03_02_01',
'ru_php_forum_52305_denis01_02',
'ru_php_forum_52305_prc_03_01_hex_01',
'ru_php_forum_52305_prc_03_02_hex_01',
'ru_php_forum_52305_prc_03_02_01_hex_01',
);
CFG::$alloc = false;
foreach ($datas as $d) {
require_once './ips/data_'.$d.'.php';
$ips_cnt = count($ips);
$ips_unq = count(array_unique($ips));
CFG::$idone = $ips_cnt * CFG::$rounds;
echo sprintf("\n"
. "BANK : %8s\n"
. "CNT : %8d\n"
. "UNQ : %8d\n"
. "ROUNDS : %8d\n"
. "BASE : %8s\n"
, $d, $ips_cnt, $ips_unq, CFG::$rounds, (CFG::$alloc ? 'INIT' : 'COPY')
);
foreach($tests2 as $test) {
$test::test();
}
}
И после-после последнего:
Code: Select all
Core2Quad Q6600 2.4GHz / 8Gb PC2-6400
Linux *** 3.13.0-46-generic #79-Ubuntu SMP Tue Mar 10 20:06:50 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux
PHP 5.5.9-1ubuntu4.6 (cli) (built: Feb 13 2015 19:17:11)
Copyright (c) 1997-2014 The PHP Group
Zend Engine v2.5.0, Copyright (c) 1998-2014 Zend Technologies
with Zend OPcache v7.0.3, Copyright (c) 1999-2014, by Zend Technologies
Просто цифры...
Code: Select all
BANK : 0
CNT : 9
UNQ : 3
ROUNDS : 10000
BASE : INIT
ru_php_forum_52305_INETCHIK_01 : 3.870133 0.000043 661592 fail-1
ru_php_forum_52305_INETCHIK_02 : 4.122872 0.000046 661592 fail-1
ru_php_forum_52305_strstr_01 : 3.154187 0.000035 663256 softfail
ru_php_forum_52305_preg_replace_callback_01_01 : 3.475129 0.000039 663256 fail-1
ru_php_forum_52305_preg_replace_callback_02_01 : 3.679745 0.000041 663856 ok
ru_php_forum_52305_preg_replace_callback_03_01 : 6.453295 0.000072 665680 ok
ru_php_forum_52305_preg_replace_callback_03_02 : 6.373738 0.000071 666208 ok
ru_php_forum_52305_preg_replace_callback_03_02_01 : 6.217962 0.000069 666208 ok
ru_php_forum_52305_mahmuzar_01 : 6.193531 0.000069 666208 ok, NOTICE
ru_php_forum_52305_mahmuzar_02 : 5.141390 0.000057 666208 ok, NOTICE
ru_php_forum_52305_mahmuzar_03 : 3.445506 0.000038 666208 ok, NOTICE
ru_php_forum_52305_denis01_02 : 2.954315 0.000033 667184 ok
ru_php_forum_52305_prc_03_01_hex_01 : 4.538740 0.000050 670584 ok known
ru_php_forum_52305_prc_03_02_hex_01 : 4.504284 0.000050 670584 ok known
ru_php_forum_52305_prc_03_02_01_hex_01 : 4.283247 0.000048 670584 ok known90000 вызовов но только три адреса.
Размер файла в принципе маленький и меняется только при смене разрядности счетчиков.
mahmuzar попал в эталонный результат.
INETCHIK тоже, если не считать что изначальные показатели 3-1-1 куда-то исчезли - остальное посчитано правильно. Конечно это провал. Нельзя терять данные. Но он у нас тут ради спортивного интереса.
Моя 01_01 удваивает записи и потом корректно накручивает счетчики. Дважды. Провал.
Хекс в силу отсутствия правильного метода инициализации считает только свои записи. Справился, но с пометкой.
не-denis01 тоже молодец.
Code: Select all
BANK : 1
CNT : 117
UNQ : 5
ROUNDS : 1000
BASE : INIT
ru_php_forum_52305_INETCHIK_01 : 5.378934 0.000046 686184 fail-2
ru_php_forum_52305_INETCHIK_02 : 5.759252 0.000049 686184 fail-2
ru_php_forum_52305_strstr_01 : 4.228255 0.000036 686352 softfail
ru_php_forum_52305_preg_replace_callback_01_01 : 4.500838 0.000038 686352 fail-2 DQF
ru_php_forum_52305_preg_replace_callback_02_01 : 4.332482 0.000037 686352 ok
ru_php_forum_52305_preg_replace_callback_03_01 : 8.515412 0.000073 687416 ok
ru_php_forum_52305_preg_replace_callback_03_02 : 5.606118 0.000048 687568 ok
ru_php_forum_52305_preg_replace_callback_03_02_01 : 5.449189 0.000047 687568 ok
ru_php_forum_52305_mahmuzar_01 : 8.795043 0.000075 687568 fail DQF
ru_php_forum_52305_mahmuzar_02 : 10.208894 0.000087 687568 fail DQF
ru_php_forum_52305_mahmuzar_03 : 6.443252 0.000055 687568 fail DQF
ru_php_forum_52305_denis01_02 : 4.170596 0.000036 687568 ok
ru_php_forum_52305_prc_03_01_hex_01 : 5.897252 0.000050 688352 ok known
ru_php_forum_52305_prc_03_02_hex_01 : 5.907418 0.000050 688352 ok known
ru_php_forum_52305_prc_03_02_01_hex_01 : 5.695299 0.000049 688352 ok known117000 на пять адресов.
Комментарий про размер файла.
INETCHIK все посчитал. Но опять кроме 3-1-1 изначальных. Спорт.
01_01 опять всё два раза. Дисквалифицируем. 01_01 опять всё два раза. Дисквалифицируем.
Остальные реализации в норме.
Кроме mahmuzar. Его код почему-то знает только про 3 айпишника и поэтому полностью игнорирует оставшиеся два. Собственно дальше будет ровно та же картина - полная потеря данных для айпишников которых не было в изначальном списке. Тоже дисквалифицируем.
Code: Select all
BANK : 2
CNT : 123
UNQ : 10
ROUNDS : 1000
BASE : INIT
ru_php_forum_52305_INETCHIK_01 : 6.721457 0.000055 699208 fail-3 DQF
ru_php_forum_52305_INETCHIK_02 : 7.017045 0.000057 699208 fail-3 DQF
ru_php_forum_52305_strstr_01 : 5.045653 0.000041 699208 softfail
ru_php_forum_52305_preg_replace_callback_01_01 : 4.887270 0.000040 699208 DQF
ru_php_forum_52305_preg_replace_callback_02_01 : 4.565532 0.000037 699208 ok
ru_php_forum_52305_preg_replace_callback_03_01 : 6.196004 0.000050 699208 ok
ru_php_forum_52305_preg_replace_callback_03_02 : 6.244183 0.000051 699208 ok
ru_php_forum_52305_preg_replace_callback_03_02_01 : 6.092158 0.000050 699208 ok
ru_php_forum_52305_mahmuzar_01 : 6.482335 0.000053 699208 DQF
ru_php_forum_52305_mahmuzar_02 : 7.397730 0.000060 699208 DQF
ru_php_forum_52305_mahmuzar_03 : 4.793424 0.000039 699208 DQF
ru_php_forum_52305_denis01_02 : 4.917484 0.000040 699208 ok
ru_php_forum_52305_prc_03_01_hex_01 : 6.361200 0.000052 699208 ok known
ru_php_forum_52305_prc_03_02_hex_01 : 6.313532 0.000051 699208 ok known
ru_php_forum_52305_prc_03_02_01_hex_01 : 6.146749 0.000050 699208 ok knownРанее было объявлено про прикол. Вот он. 123000 вызовов на 10 адресов.
Было у меня 5 адресов. Я убрал по последней цифре у каждого из них. И получил еще 5 адресов. Например 11.11.11.11 и 11.11.11.1. К сожалению только я додумался искать в строке не только айпишник но и знак равенства - мои алгоритмы не заливают стату адреса 11.11.11.1 в стату адресов 11.11.11.11, 11.11.11.12, 11.11.11.13 и так далее. Поэтому с этого этапа реализация INETCHIK-а окончательно участвует ради строчек в бенчмарке.
Далее более интересные тесты - с большими и более произвольными списками.
Code: Select all
BANK : g0
CNT : 10000
UNQ : 217
ROUNDS : 5
BASE : INIT
ru_php_forum_52305_INETCHIK_01 : 13.857843 0.000277 3441584 DQF
ru_php_forum_52305_INETCHIK_02 : 22.517048 0.000450 3441584 DQF
ru_php_forum_52305_strstr_01 : 9.038891 0.000181 3441584
ru_php_forum_52305_preg_replace_callback_01_01 : 3.477516 0.000070 3441584 DQF
ru_php_forum_52305_preg_replace_callback_02_01 : 3.366447 0.000067 3441584
ru_php_forum_52305_preg_replace_callback_03_01 : 3.998600 0.000080 3441584
ru_php_forum_52305_preg_replace_callback_03_02 : 4.057613 0.000081 3441584
ru_php_forum_52305_preg_replace_callback_03_02_01 : 4.130979 0.000083 3441584
ru_php_forum_52305_mahmuzar_01 : 2.557624 0.000051 3441584 DQF
ru_php_forum_52305_mahmuzar_02 : 2.883417 0.000058 3441584 DQF
ru_php_forum_52305_mahmuzar_03 : 1.880305 0.000038 3441584 DQF
ru_php_forum_52305_denis01_02 : 9.633680 0.000193 3441584
ru_php_forum_52305_prc_03_01_hex_01 : 4.697958 0.000094 3441584
ru_php_forum_52305_prc_03_02_hex_01 : 6.077962 0.000122 3441584
ru_php_forum_52305_prc_03_02_01_hex_01 : 6.333923 0.000127 3441584
BANK : g1
CNT : 10000
UNQ : 1488
ROUNDS : 5
BASE : INIT
ru_php_forum_52305_INETCHIK_01 : 87.801011 0.001756 4919664 DQF
ru_php_forum_52305_INETCHIK_02 : 132.797057 0.002656 4919664 DQF
ru_php_forum_52305_strstr_01 : 53.846996 0.001077 4919664
ru_php_forum_52305_preg_replace_callback_01_01 : 13.169129 0.000263 4919664 DQF
ru_php_forum_52305_preg_replace_callback_02_01 : 12.383472 0.000248 4919664
ru_php_forum_52305_preg_replace_callback_03_01 : 13.058233 0.000261 4919664
ru_php_forum_52305_preg_replace_callback_03_02 : 13.490592 0.000270 4919664
ru_php_forum_52305_preg_replace_callback_03_02_01 : 14.267716 0.000285 4919664
ru_php_forum_52305_mahmuzar_01 : 2.552806 0.000051 4919664 DQF
ru_php_forum_52305_mahmuzar_02 : 2.938797 0.000059 4919664 DQF
ru_php_forum_52305_mahmuzar_03 : 1.875671 0.000038 4919664 DQF
ru_php_forum_52305_denis01_02 : 67.447713 0.001349 4919664
ru_php_forum_52305_prc_03_01_hex_01 : 18.196488 0.000364 4919664
ru_php_forum_52305_prc_03_02_hex_01 : 16.626948 0.000333 4919664
ru_php_forum_52305_prc_03_02_01_hex_01 : 14.505341 0.000290 4919664
BANK : g2
CNT : 10000
UNQ : 6921
ROUNDS : 5
BASE : INIT
ru_php_forum_52305_INETCHIK_01 : 395.248355 0.007905 4925080 DQF
ru_php_forum_52305_INETCHIK_02 : 826.754699 0.016535 4925080 DQF
ru_php_forum_52305_strstr_01 : 380.741465 0.007615 4925080
ru_php_forum_52305_preg_replace_callback_01_01 : 71.081354 0.001422 4925080 DQF
ru_php_forum_52305_preg_replace_callback_02_01 : 69.636063 0.001393 4925080
ru_php_forum_52305_preg_replace_callback_03_01 : 71.205819 0.001424 4925080
ru_php_forum_52305_preg_replace_callback_03_02 : 73.955237 0.001479 4925080
ru_php_forum_52305_preg_replace_callback_03_02_01 : 75.101566 0.001502 4925080
ru_php_forum_52305_mahmuzar_01 : 3.623693 0.000072 4925080 DQF
ru_php_forum_52305_mahmuzar_02 : 3.243513 0.000065 4925080 DQF
ru_php_forum_52305_mahmuzar_03 : 1.853292 0.000037 4925080 DQF
ru_php_forum_52305_denis01_02 : 519.210724 0.010384 4925080
ru_php_forum_52305_prc_03_01_hex_01 : 44.325944 0.000887 4925080
ru_php_forum_52305_prc_03_02_hex_01 : 47.474540 0.000949 4925080
ru_php_forum_52305_prc_03_02_01_hex_01 : 52.828695 0.001057 4925080По 50000 вызовов для 217-1488-6921 уника соответственно. Не смотрите на суперские результаты mahmuzar - его код работает только с айпишниками которые уже вписаны в файл и ВООБЩЕ ВСЕ новые тупо пролетают мимо.
А вот регулярки начинают огрызаться на построчную итерацию.
Напомню 02_01 быстра отсутствием необходимости блокировать файл.
Обязательные пробелы надувают файл а это значит надо больше текста читать из файла, прогонять регуляркой, записывать в файл. Пара байт (по пробелу вокруг знака равенства) на 7000 строк это 14000 байт лишнего текста.
Реализации 03_02_01 вполне логично тормозят - им приходится лишний раз искать вхождение строки перед возможным запуском регулярки. И чем больше записей в файле тем дольше идет поиск.
Осталось только показать цифры для изначально больших файлов.
Code: Select all
BANK : 0
CNT : 10000
UNQ : 6921
ROUNDS : 5
BASE : COPY
ru_php_forum_52305_INETCHIK_01 : 413.891201 0.008278 4925080 DQF
ru_php_forum_52305_strstr_01 : 437.573011 0.008751 5033496
ru_php_forum_52305_preg_replace_callback_01_01 : 70.348374 0.001407 5033496 DQF
ru_php_forum_52305_preg_replace_callback_02_01 : 54.230805 0.001085 5033496
ru_php_forum_52305_preg_replace_callback_03_01 : 76.500927 0.001530 5033496
ru_php_forum_52305_preg_replace_callback_03_02 : 79.567291 0.001591 5033496
ru_php_forum_52305_preg_replace_callback_03_02_01 : 79.572252 0.001591 5033496Собственно что тут произошло? Ну ладно 413-437 это мне не сложно было подождать. Я просто тормознул тест на 1416-ой секунде работы алгоритма mahmuzar_01 - начали заканчиваться 10 гигабайт места на диске. И я даже не знаю почему...
Вот почему как уже было сказано выше - я оставил для COPY-теста только работающий код.
Небольшое увеличение по всем фронтам в тестах 0-1-2 связано с тем что я занял сервак другой обработкой не отвлекаясь от этой. Извините, приоритеты.
Code: Select all
BANK : 0
CNT : 9
UNQ : 3
ROUNDS : 10000
BASE : COPY
ru_php_forum_52305_preg_replace_callback_03_01 : 134.456337 0.001494 944688
ru_php_forum_52305_preg_replace_callback_03_02 : 99.816503 0.001109 945344
ru_php_forum_52305_preg_replace_callback_03_02_01 : 92.148603 0.001024 1044432
ru_php_forum_52305_denis01_02 : 341.410875 0.003793 1838440
ru_php_forum_52305_prc_03_01_hex_01 : 137.035402 0.001523 1838440
ru_php_forum_52305_prc_03_02_hex_01 : 94.545498 0.001051 1838440
ru_php_forum_52305_prc_03_02_01_hex_01 : 123.672071 0.001374 1838440
BANK : 1
CNT : 117
UNQ : 5
ROUNDS : 1000
BASE : COPY
ru_php_forum_52305_preg_replace_callback_03_01 : 182.791637 0.001562 1838440
ru_php_forum_52305_preg_replace_callback_03_02 : 132.566221 0.001133 1838440
ru_php_forum_52305_preg_replace_callback_03_02_01 : 200.064098 0.001710 1838440
ru_php_forum_52305_denis01_02 : 726.709365 0.006211 1927816
ru_php_forum_52305_prc_03_01_hex_01 : 169.096030 0.001445 1927816
ru_php_forum_52305_prc_03_02_hex_01 : 168.650735 0.001441 1927816
ru_php_forum_52305_prc_03_02_01_hex_01 : 211.011557 0.001804 1927816
BANK : 2
CNT : 123
UNQ : 10
ROUNDS : 1000
BASE : COPY
ru_php_forum_52305_preg_replace_callback_03_01 : 184.295725 0.001498 1927816
ru_php_forum_52305_preg_replace_callback_03_02 : 185.558124 0.001509 1927816
ru_php_forum_52305_preg_replace_callback_03_02_01 : 195.600845 0.001590 1927816
ru_php_forum_52305_denis01_02 : 914.613231 0.007436 1999600
ru_php_forum_52305_prc_03_01_hex_01 : 182.474893 0.001484 1999600
ru_php_forum_52305_prc_03_02_hex_01 : 172.108521 0.001399 1999600
ru_php_forum_52305_prc_03_02_01_hex_01 : 210.593596 0.001712 1999600
BANK : g0
CNT : 10000
UNQ : 217
ROUNDS : 5
BASE : COPY
ru_php_forum_52305_preg_replace_callback_03_01 : 84.114852 0.001682 3388424
ru_php_forum_52305_preg_replace_callback_03_02 : 84.218770 0.001684 3388424
ru_php_forum_52305_preg_replace_callback_03_02_01 : 90.425789 0.001809 3388424
ru_php_forum_52305_denis01_02 : 350.144183 0.007003 3487864
ru_php_forum_52305_prc_03_01_hex_01 : 82.383787 0.001648 3487864
ru_php_forum_52305_prc_03_02_hex_01 : 83.376514 0.001668 3487864
ru_php_forum_52305_prc_03_02_01_hex_01 : 93.390521 0.001868 3487864
BANK : g1
CNT : 10000
UNQ : 1488
ROUNDS : 5
BASE : COPY
ru_php_forum_52305_preg_replace_callback_03_01 : 90.086613 0.001802 4857136
ru_php_forum_52305_preg_replace_callback_03_02 : 73.125031 0.001463 4857136
ru_php_forum_52305_preg_replace_callback_03_02_01 : 67.266207 0.001345 4857136
ru_php_forum_52305_denis01_02 : 424.768950 0.008495 4857136
ru_php_forum_52305_prc_03_01_hex_01 : 91.502942 0.001830 4857136
ru_php_forum_52305_prc_03_02_hex_01 : 92.499745 0.001850 4857136
ru_php_forum_52305_prc_03_02_01_hex_01 : 111.306154 0.002226 4857136
BANK : g2
CNT : 10000
UNQ : 6921
ROUNDS : 5
BASE : COPY
ru_php_forum_52305_preg_replace_callback_03_01 : 77.004190 0.001540 4886552
ru_php_forum_52305_preg_replace_callback_03_02 : 81.621627 0.001632 4886552
ru_php_forum_52305_preg_replace_callback_03_02_01 : 89.057038 0.001781 4886552
ru_php_forum_52305_denis01_02 : 507.786773 0.010156 4886552
ru_php_forum_52305_prc_03_01_hex_01 : 75.289561 0.001506 4886552
ru_php_forum_52305_prc_03_02_hex_01 : 78.195882 0.001564 4886552
ru_php_forum_52305_prc_03_02_01_hex_01 : 93.307614 0.001866 4886552
Что я тут вижу?
- Регулярки хорошо справились с задачей.
- Пробелы замедляют работу ибо размер данных увеличивается и на всех этапах - чтение файла, выполнение регулярки, запись в файл.
- Это всего несколько секунд на несколько десятков тысяч вызовов. Обычная загрузка центрального процессора другими задачами куда сильнее тормозит процессы.
- А с пробелами кстати файлы красивее.
- Идея упаковки в хекс на мой взгляд экономит скорее дисковое пространство. По крайней мере по таким небольшим дельтам времён (впрочем как в одну так и в другую сторону) - я бы не бросился делать файл "базы" менее читаемым.
- Поиск подстроки насасывает.
- Построчная итерация файла с поиском подстроки см предыдущий пункт.
- В результатах есть два кода, которые не имеют описания:
- ru_php_forum_52305_INETCHIK_02: автор предложил вторую реализацию алгоритма, но цифры подсказывают что лучше б он так не делал.
Code: Select all
class ru_php_forum_52305_INETCHIK_02 extends ru_php_forum_52305 {
public static function inc ($ip) {
$ar=file(static::FILE);
if(strpos(file_get_contents(static::FILE), $ip)===false){
$data="$ip=1" . PHP_EOL;
}
else{
foreach ($ar as $k=>$v) {
if(strpos($v, $ip) !==false) {
$sub=trim(substr($v, strlen($ip)+1));
$ar[$k]=$ip.'='.($sub+1). PHP_EOL;
$data=$ar[$k];
break;
}
}
}
array_unshift($ar, $data);
file_put_contents(static::FILE, array_unique($ar));
}
} - ru_php_forum_52305_strstr_01: конечно помимо регулярок и того кода, который-не-писал-denis01, я пытался познать алгоритм построчной итерации файла и наклепал вот эту хрень:В тестах помечена как softfail потому что она расслаивает данные, если вдруг в строке пробел слева от знака равенства. Данные не потеряны, но требуют обработки. Фу-фу-фу. Впрочем, у других данные вообще теряются. И в среднем теряют ещё и дольше чем этот алгоритм.
Code: Select all
class ru_php_forum_52305_strstr_01 extends ru_php_forum_52305 {
public static function inc ($ip) {
$f = file(static::FILE, FILE_SKIP_EMPTY_LINES | FILE_IGNORE_NEW_LINES);
$done = false;
$IP = $ip . '=';
foreach ($f as &$s) {
if (false !== strstr($s, $IP)) {
$s = $ip . '=' .
(1 + array_slice(
explode('=', $s)
, 1, 1)[0]
);
$done = true;
}
}
if (!$done) {
$f[] = $ip . '=1';
}
file_put_contents(static::FILE, implode(PHP_EOL, $f));
}
}
- ru_php_forum_52305_INETCHIK_02: автор предложил вторую реализацию алгоритма, но цифры подсказывают что лучше б он так не делал.
- Для нескольких тысяч строк счетчиков - вполне хватит. Для бОльшего... Наверное лучше пробовать разбивать "базу" на несколько файлов. Но тогда почему бы вообще сразу не перейти на более подходящий инструмент?
Спасибо за внимание.
ЗЫ. Вообще мой никнейм 11 лет и 2 дня назад задумывался как Газаль, но за последние несколько месяцев меня постепенно приучили к жесткому Л на конце.
Теперь мне интересно сколько людей это прочитает хотя бы наполовину.