О чем этот пост? Данный пост рассматривает бенчмарк нескольких подходов к решению задачи озвученной в посте Поиск ключа по ini файлу. Букв будет очень много. Можно сразу прокрутить до главы "Просто цифры" но тогда будет не понятно что некоторые из них означают. Чо тестировали? В тесте приняли участие несколько реализаций (кодовое имя: описание): INETCHIK_01: алгоритм предложенный пользователем INETCHIK в посте 419045 Модификация кода: Код (PHP): 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; } } } выходной вариант: Код (PHP): 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; } } } } } [/*:m] mahmuzar_01, mahmuzar_02, mahmuzar_03: алгоритмы пользователя mahmuzar из постов 418894 (01, 02) и 418988 (03). Расписывать дельту не буду ибо там всего пара замен: 1) учет того что айпишник аргументом, 2) путь к файлу и 3) убрано лишнее общение скрипта с человеком. Первая реализация первого поста: Код (PHP): <?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); } } Вторая реализация первого поста - автор добавил блокировку файла: Код (PHP): 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); } } Ну и третья реализация, из второго поста: Код (PHP): 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); //закрываем открытый дескриптор файла } } } [/*:m] denis01_02 - на самом деле мало отношения имеет к denis01. Бегло читая обсуждение сходил по ссылке и скопировал код, понял что его алгоритм не совсем ту задачу решает, но было интересно увидеть производительность посему решил немного переписать под себя. Если denis01 захочет - кодовое имя denis01_01 зарезервировано под его вариант. Код (PHP): <?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)); } } [/*:m][/list:u] На этом уходим в глубокое... Лирическое отступление. Товарищь INETCHIK просил о бенчмарке алгоритма поэтому лично я не приравниваю его код к решению задачи и допускаю что код может некорректно работать с данными. На самом деле автор может спать спокойно - результаты его алгоритма близки к эталону. Ошибка мелкая, но эту ошибку допустили все участники. Даже не поверите я. Но в данном случае ошибка приводит к невосстановимой потере данных.[/*:m] Более крупная ошибка это лишние чтения. Тут уже пардоньте: как можно на бенчмарк предлагать код который ДВАЖДЫ читает файл? Это уже бутылка, разве нет? [/*:m] mahmuzar честно говоря огорчил. Все три кода высыпаются изобилием нотисов (да, не ворнингов, как я писал в основной теме), алгоритм теряет данные, алгоритм совершает лишние действия. Но автор предложил этот код именно как решение задачи. ИМХО ну нельзя предлагать что-то сырое недоделанное в данном случае. Мы говорим не о куске кода который может быть полезен в другом проекте а именно о завершенном алгоритме который должен решать сформулированную задачу. Опять же прикрываться статусом новичка в данном случае считаю неприемлемо. Можно опубликовать с просьбой рассмотреть алгоритм опытными участниками но не постить "на отвяжитесь" (да, тут должно быть другое слово но мы не в подворотне) якобы готовое решение. Оно не готово. Банально потому что сам автор его не отлаживал. А огорчил тем что считает это нормой. [/*:m] Весь сыр-бор с бенчмарком я начал из-за моего же предложения использовать preg_replace_callback() для решения поставленной задачи. Честно я удивлен что никто не взял на себя смелость предложить рабочий алгоритм с использованием этой функции.[/*:m] А зря... Давайте подумаем над решением задачи. Её алгоритм сводится к набору следующих действий: Открыли файл.[/*:m] Почитали в поисках записи.[/*:m] Если нашли - инкремент счетчика.[/*:m] Если не нашли - записали строку со счетчиком равным единице.[/*:m] Сохранили файл.[/*:m][/list] Я тут не увидел ни одной реализации которая бы работала с данными именно на уровне файла. И это нормально (лениво расписывать почему). Зато все алгоритмы целиком (и это тоже хорошо) считывают файл в окружение скрипта. А дальше ищут, инкрементируют или дописывают, сохраняют. Почему для этих целей нельзя использовать регулярные выражения? Мы ищем некоторую строку которая должна содержать в себе переданный айпишник, знак равенстра, сколько-то цифр которые мы за счетчик примем. Бдыщь: Код (Text): ^IP=\d+$ Человек случайно ставит пробелы вокруг равно? Бдыщь: Код (Text): ^IP\s*=\s*\d+$ Человек уверен что вокруг равенства будет только по одному пробелу? Ну и пусть: Код (Text): ^IP = \d+$ И моё предположение было таковым: вместо того чтоб построчно итерировать содержимое файла и в каждой строке искать айпишник и делать над ним какие-то действия - быстрее будет по регулярному выражению найти вхождение айпишника и его счетчика. И в пхп-поставке регулярных выражений есть замечательная функция которая позволит сразу при нахождении вхождения айпишника соврешить инкремент его счетчика и при этом всю тягомотину с текстом "до" и с текстом "после" возьмет на себя. А если не будет вхождения то мы по традиции допишем. И я наступил на грабли: Код (PHP): $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(). Если пробелов не будет - код работает. Если будет - код просто добавляет каждый раз новую строку и всё. Это называется софтфейл. Данные не теряются. Их можно пересчитать. Но поскольку счетчики плывут - таки фейл. Да и зачем вообще искать вхождение строки если само регулярное выражение этим занимается? Но как тогда узнать было ли вхождение? А вот так: Код (PHP): $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); Ну и естественно раз уж я сам упомянул состояние гонки - некоторые реализации содержат блокировку файла.[/*:m][/list:u] Конец лирического отступления. Чо тестировали? Часть Ганзала. Напомню что я-сука-такая один из своих исходников подпихнул другому автору, но честно признал подставу. Для тех кто не читал: речь о листинге denis01_02 Итак, полет моей фантазии: ru_php_forum_52305_preg_replace_callback_01_01: первый вариант. Пишет новые строки если есть пробелы вокруг равенства: Код (PHP): 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); } } [/*:m] ru_php_forum_52305_preg_replace_callback_02_01: второй вариант. Красавчик вообще: Код (PHP): 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); } } [/*:m] ru_php_forum_52305_preg_replace_callback_03_01: третий заход. С блокировкой файла. Код (PHP): 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); } } [/*:m] ru_php_forum_52305_preg_replace_callback_03_02: третий заход, второй подзаход. Проверил что будет если мы решили что вокруг равенства будет только по одному пробелу. То есть меняется только три строки: Код (PHP): $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'; # и эта } [/*:m] ru_php_forum_52305_preg_replace_callback_03_02_01: шизофрения 3-2-1. Решил проверить что будет с функцией strstr() если она тоже будет знать о фиксированном формате. Измененные строки отмечены: Код (PHP): } // 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); [/*:m][/list:u] Чо тестировали? Часть Ганзала. Вне конкурса. Естественно я заметил очевидную вещч - чем больше строк тем толще файл. Ваш КО. Но надо ли нам тратить много места на хранение айпишника? Самый длинный айпишник в в4 описывается 15 байтами текста хотя на самом деле это всего 4 байта. Поэтому я решил протестировать еще 3 подхода в которых айпишник сохраняется хексом и занимает всегда 8 байт. Добавляем одну строку упаковки айпи и в двух строках меняем название переменной $ip на $IP: Код (PHP): } // 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); формирование имен без энтузиазма 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. Чо не тестировали? Не стал я писать реализацию где айпишних хранится 4 байтами и счетчик хранится тоже 4 байтами и по всему этому удобно итерируемся с офсетом в 8 байт. Хекс-реализация уже потребует конвертирования айпишника, но по крайней мере человекочитаемая база остается. А тут уже без утилит не прочитаешь.[/*:m] Не стал писать на скуэльлайте. Вроде и файл. Вроде и удобно языком структурированных запросов. Но без сторонних программ в этот файл нормально не посмотришь. Отпадает.[/*:m] Не стал рассматривать NoSQL вроде мемкэш рэдис монго и тп. У нас тут задача с примитивным файлом и кажется человек его хочет в блокноте открывать. Без лишнего апи.[/*:m][/list:u] Как тестировали? Примитивно. Создали наборы айпишников и передавали их в метод inc() каждого класса. Каждый набор ддосил метод в несколько раундов. Чтоб в меня не полетела стружка с жесткого диска - файл "базы" хранился на рам-диске. Наборы адресов имеют следующие имена и свойства: 0: 9 зайписей для 3 уникальный айпишников скопированных из поста - 11бла 44бла и 88бла. Статистика по элементам Код (Text): e = 2 n = 2 cc = 4 cs = 4 e = 5 n = 1 cc = 5 cs = 9 читается эта лабуда наверное так: по 2 вхождения, 2 адреса, суммарно вызовов 4 (дваджы-два-же-ж), контрольная сумма 4 по 5 вхождений, 1 адрес, суммарно вызовов 5, кс 9 (пять плюс четыре) на деле речь о массиве: Код (PHP): $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', ); [/*:m] 1: 117 раз для всего лишь 5 айпишников. Стат Код (PHP): 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 [/*:m] 2: прикол который кладет половину реализаций. 123 раза, 10 уников Код (PHP): 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 записей с разным кол-вом уникальных и повторных записей: [/*:m] g0: 217 уников размазались как Код (PHP): 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 [/*:m] g1: 1488 (оно само! ранодомом! честно!) уников Код (PHP): 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 [/*:m] g2: 6921 уник Код (PHP): 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 [/*:m][/list:u] Предпоследнее что нужно знать перед чтением результатов это что за BASE такой. Это два режима с которым тест начинает насиловать файл. Если перед нами INIT значит сценарий записал 3 айпишника как это предоставил автор темы. А строка COPY значит что мы перед тестом копируем уже готовый толстый файл с 6900+ адресов и начинаем тупо накручивать у них счетчики. Зачем? Если изначально база пустая и в нее налетают уники - она постепенно растет. И производительность полного цикла уменьшается. Поэтому результат INIT-теста в самом начале практически как скорость фотона но постепенно замедляется. А среднюю мы считаем по всему тесту. А вот COPY практически не будет изменять размер файла. Только когда десятки-сотни-тысячи будут лишний разряд накидывать. Следовательно весь тест будет более равномерным. После воодушевляющих результатов чужих реализаций решил COPY-тест проводить только со своими алгоритмами. Я честно запускал их но при результатах допустим в 1416 секунд на один тест - я бы всю эту херню, которую никто не прочитает, закончил бы только к апрелю. Я уж молчу про гигабайты нотисов mahmuzar-а - и так понятно что алгоритм не работает и не вижу смысла его дальше насиловать. Ну и последнее - код класса который мы расширияем всей толпой: Код (PHP): 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 Код (PHP): 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(); } } И после-после последнего: Код (Text): 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 Просто цифры... Код (Text): 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 known 90000 вызовов но только три адреса. Размер файла в принципе маленький и меняется только при смене разрядности счетчиков. mahmuzar попал в эталонный результат. INETCHIK тоже, если не считать что изначальные показатели 3-1-1 куда-то исчезли - остальное посчитано правильно. Конечно это провал. Нельзя терять данные. Но он у нас тут ради спортивного интереса. Моя 01_01 удваивает записи и потом корректно накручивает счетчики. Дважды. Провал. Хекс в силу отсутствия правильного метода инициализации считает только свои записи. Справился, но с пометкой. не-denis01 тоже молодец. Код (Text): 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 known 117000 на пять адресов. Комментарий про размер файла. INETCHIK все посчитал. Но опять кроме 3-1-1 изначальных. Спорт. 01_01 опять всё два раза. Дисквалифицируем. 01_01 опять всё два раза. Дисквалифицируем. Остальные реализации в норме. Кроме mahmuzar. Его код почему-то знает только про 3 айпишника и поэтому полностью игнорирует оставшиеся два. Собственно дальше будет ровно та же картина - полная потеря данных для айпишников которых не было в изначальном списке. Тоже дисквалифицируем. Код (Text): 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-а окончательно участвует ради строчек в бенчмарке. Далее более интересные тесты - с большими и более произвольными списками. Код (Text): 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 вполне логично тормозят - им приходится лишний раз искать вхождение строки перед возможным запуском регулярки. И чем больше записей в файле тем дольше идет поиск. Осталось только показать цифры для изначально больших файлов. Код (Text): 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 связано с тем что я занял сервак другой обработкой не отвлекаясь от этой. Извините, приоритеты. Код (Text): 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 Что я тут вижу? Регулярки хорошо справились с задачей.[/*:m] Пробелы замедляют работу ибо размер данных увеличивается и на всех этапах - чтение файла, выполнение регулярки, запись в файл.[/*:m] Это всего несколько секунд на несколько десятков тысяч вызовов. Обычная загрузка центрального процессора другими задачами куда сильнее тормозит процессы.[/*:m] А с пробелами кстати файлы красивее.[/*:m] Идея упаковки в хекс на мой взгляд экономит скорее дисковое пространство. По крайней мере по таким небольшим дельтам времён (впрочем как в одну так и в другую сторону) - я бы не бросился делать файл "базы" менее читаемым.[/*:m] Поиск подстроки насасывает.[/*:m] Построчная итерация файла с поиском подстроки см предыдущий пункт.[/*:m] В результатах есть два кода, которые не имеют описания: ru_php_forum_52305_INETCHIK_02: автор предложил вторую реализацию алгоритма, но цифры подсказывают что лучше б он так не делал. Код (PHP): 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)); } } [/*:m] ru_php_forum_52305_strstr_01: конечно помимо регулярок и того кода, который-не-писал-denis01, я пытался познать алгоритм построчной итерации файла и наклепал вот эту хрень: Код (PHP): 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)); } } В тестах помечена как softfail потому что она расслаивает данные, если вдруг в строке пробел слева от знака равенства. Данные не потеряны, но требуют обработки. Фу-фу-фу. Впрочем, у других данные вообще теряются. И в среднем теряют ещё и дольше чем этот алгоритм.[/*:m][/list:u][/*:m] Для нескольких тысяч строк счетчиков - вполне хватит. Для бОльшего... Наверное лучше пробовать разбивать "базу" на несколько файлов. Но тогда почему бы вообще сразу не перейти на более подходящий инструмент?[/*:m][/list:u] Спасибо за внимание. ЗЫ. Вообще мой никнейм 11 лет и 2 дня назад задумывался как Газаль, но за последние несколько месяцев меня постепенно приучили к жесткому Л на конце. Теперь мне интересно сколько людей это прочитает хотя бы наполовину.
Побольше бы тут подобных тестов, и соревнований. Браво. Прочитал усе, до последней строчки. Надо работать надо собой) По поводу кода: А что требовалось? разве не это?) нету в базе, значит пропускаем, и записываем прочтенную строку в файл обратно, не?. Или я что-то не понимаю.
Дядь, давай поразмышляем логически))) Если твой код медленно НЕ добавляет то что с ним будет если он станет добавлять? =)
что-то я не пойму.. Если примеры работают с одним и тем же файлом, то почему когда мой не добавляет, твой должен добавлять изменения в файл?))
все мои алгоритмы добавляют. я для себя задачу понял как "есть - плюс, нет - добавить". и ты наверное понимаешь что если задача будет "плюс если есть" то я напишу алгоритм вида Код (PHP): $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) { file_put_contents(static::FILE, $f); } и не буду вообще трогать файл на запись если его содержимое не меняется.
сам удивился, увидев, что результат не меняется. А у вас меняется. Получается мой код работает иначе. По условию, добавить не говорится, поэтому пропускал.
мне вариант с регулярками нравится больше. ибо он быстрый и гибкий. но в качестве доп. варианта, предложу такой(он жестко привязан к структуре файла, подразумевая что там ДО и ПОСЛЕ символа '=' ЕСТЬ пробелы) Код (PHP): $ip = '88.88.88.88'; $file = 'ip.txt'; // line format: IPV4 = \d+ // $s = file_get_contents($file); if ( ($p = strpos($s, $ip)) !==false ) { $p_cnt = $p +strlen($ip) +3; $p_nl = strpos($s, PHP_EOL, $p_cnt); $cnt = intval( $p_nl!==false ? substr($s, $p_cnt, $p_nl-$p_cnt) : substr($s, $p_cnt) ); file_put_contents($file, substr_replace($s, $cnt+1, $p_cnt, strlen($cnt)), LOCK_EX ); } else { // new ip file_put_contents($file, $ip.' = 1'.PHP_EOL, LOCK_EX | FILE_APPEND ); }
кстати. или я чегото не понимаю. или у тебя тут большой глюк. при добавлении нового адреса, он его добавляет в конец файла. если далее мы меняем адрес на предыдущий(например), но вместо того чтоб заменить его в строке, добавляет его еще раз. в конце файла. это неприемлемо. если изначально файл пустой, то при вызове добавляется пустая строка, и потом только - адрес и счетчик... зачем? Добавлено спустя 30 минут 3 секунды: Ganzal, кароч, ты заюзал модификатор m - похоже, плохо понимая принцип его работы. что тут сказать? ))) читай мануал) и исправляй свой код.
согласен, непреемлемо. но ни один мой листинг так не делает. ты про какой? собственно я ниже покажу тест а ты пальцем ткни. ну это я может быть перенервничал при добавлении константы. как ты понимаешь это на работу вообще никак не влияет. тест ниже. я всю свою сознательную жизнь считаю что модификатор m включает режим поиска по всему значению переменной и тогда ^ и $ ищут соответственно начала строк и их окончания. сделал ~^IP = (\d+)$~ - перфоманс просел во много раз. сделал ~IP = (\d+)~ - перфоманс незначительно но всё же просел. честно может я чего-то недопонимаю в регулярках. ткни пальцев и на примерах расскажи почему тут нельзя использовать модификатор m листинг #1 - проседание перфоманса ~^IP = (\d+)$~ то есть без m-модификатора. Код (Text): ru_php_forum_52305_preg_replace_callback_02_02 : 4.514682 0.000050 577488 ru_php_forum_52305_preg_replace_callback_03_02 : 6.192382 0.000069 578512 ru_php_forum_52305_preg_replace_callback_04_fix_flock : 118.919845 0.001321 4883376 ru_php_forum_52305_preg_replace_callback_04_fix : 129.900431 0.001443 4883376 листинг 2 - в моей 04 регулярка заменена на ~IP = (\d+)~ то есть убран не только модификатор m но и указатели на начало-конец строки. заодно проверяем код runcore. кстати портировал я его так: Код (PHP): <?php class ru_php_forum_52305_runcore_01 extends ru_php_forum_52305 { public static function inc ($ip) { $s = file_get_contents(static::FILE); if ( ($p = strpos($s, $ip)) !== false ) { $p_cnt = $p +strlen($ip) +3; $p_nl = strpos($s, PHP_EOL, $p_cnt); $cnt = intval( $p_nl!==false ? substr($s, $p_cnt, $p_nl-$p_cnt) : substr($s, $p_cnt) ); file_put_contents(static::FILE, substr_replace($s, $cnt+1, $p_cnt, strlen($cnt)), LOCK_EX ); } else { // new ip file_put_contents(static::FILE, $ip.' = 1'.PHP_EOL, LOCK_EX | FILE_APPEND ); } } } результаты: Код (Text): BANK : 0 CNT : 9 UNQ : 3 ROUNDS : 10000 BASE : INIT ru_php_forum_52305_strstr_01 : 3.171433 0.000035 583528 ru_php_forum_52305_preg_replace_callback_02_02 : 3.189578 0.000035 584656 ru_php_forum_52305_preg_replace_callback_03_02 : 4.205186 0.000047 585856 ru_php_forum_52305_preg_replace_callback_04_fix_flock : 4.127870 0.000046 586496 ru_php_forum_52305_preg_replace_callback_04_fix : 3.081571 0.000034 586496 ru_php_forum_52305_denis01_02 : 2.948388 0.000033 586496 ru_php_forum_52305_runcore_01 : 2.624432 0.000029 586496 BANK : 1 CNT : 117 UNQ : 5 ROUNDS : 1000 BASE : INIT ru_php_forum_52305_strstr_01 : 4.242355 0.000036 604192 ru_php_forum_52305_preg_replace_callback_02_02 : 4.153071 0.000035 604464 ru_php_forum_52305_preg_replace_callback_03_02 : 5.510890 0.000047 605128 ru_php_forum_52305_preg_replace_callback_04_fix_flock : 5.423911 0.000046 605128 ru_php_forum_52305_preg_replace_callback_04_fix : 4.032159 0.000034 605128 ru_php_forum_52305_denis01_02 : 4.124032 0.000035 605128 ru_php_forum_52305_runcore_01 : 3.404929 0.000029 605128 BANK : 2 CNT : 123 UNQ : 10 ROUNDS : 1000 BASE : INIT ru_php_forum_52305_strstr_01 : 5.006127 0.000041 616776 ru_php_forum_52305_preg_replace_callback_02_02 : 4.502291 0.000037 616776 ru_php_forum_52305_preg_replace_callback_03_02 : 6.035958 0.000049 616776 ru_php_forum_52305_preg_replace_callback_04_fix_flock : 5.948806 0.000048 616776 ru_php_forum_52305_preg_replace_callback_04_fix : 4.498139 0.000037 616776 ru_php_forum_52305_denis01_02 : 7.007652 0.000057 616776 ru_php_forum_52305_runcore_01 : 5.054683 0.000041 616776 BANK : g0 CNT : 10000 UNQ : 217 ROUNDS : 5 BASE : INIT ru_php_forum_52305_strstr_01 : 13.191535 0.000264 3360280 ru_php_forum_52305_preg_replace_callback_02_02 : 5.127474 0.000103 3360280 ru_php_forum_52305_preg_replace_callback_03_02 : 6.081731 0.000122 3360280 ru_php_forum_52305_preg_replace_callback_04_fix_flock : 8.747257 0.000175 3360280 ru_php_forum_52305_preg_replace_callback_04_fix : 7.788782 0.000156 3360280 ru_php_forum_52305_denis01_02 : 14.836580 0.000297 3360280 ru_php_forum_52305_runcore_01 : 2.366116 0.000047 3360280 BANK : g1 CNT : 10000 UNQ : 1488 ROUNDS : 5 BASE : INIT ru_php_forum_52305_strstr_01 : 74.099817 0.001482 4836424 ru_php_forum_52305_preg_replace_callback_02_02 : 19.241393 0.000385 4836424 ru_php_forum_52305_preg_replace_callback_03_02 : 20.174599 0.000403 4836424 ru_php_forum_52305_preg_replace_callback_04_fix_flock : 35.618944 0.000712 4836424 ru_php_forum_52305_preg_replace_callback_04_fix : 34.687058 0.000694 4836424 ru_php_forum_52305_denis01_02 : 77.375614 0.001548 4836424 ru_php_forum_52305_runcore_01 : 2.897272 0.000058 4836424 BANK : g2 CNT : 10000 UNQ : 6921 ROUNDS : 5 BASE : INIT ru_php_forum_52305_strstr_01 : 341.792648 0.006836 4868864 ru_php_forum_52305_preg_replace_callback_02_02 : 48.996806 0.000980 4868864 ru_php_forum_52305_preg_replace_callback_03_02 : 52.620071 0.001052 4868864 ru_php_forum_52305_preg_replace_callback_04_fix_flock : 65.520326 0.001310 4868864 ru_php_forum_52305_preg_replace_callback_04_fix : 68.319577 0.001366 4868864 ru_php_forum_52305_denis01_02 : 585.529199 0.011711 4868864 ru_php_forum_52305_runcore_01 : 11.719550 0.000234 4868864 круто, но... код runcore теряет стату... проводим маленькое тестирование. его же проводим из-за замечания на пустой файл и неверный конец строки. код теста: Код (PHP): require_once 'ru_php_forum_52305.php'; $tests = array( 'ru_php_forum_52305_preg_replace_callback_03_02', 'ru_php_forum_52305_preg_replace_callback_03_02_eol', 'ru_php_forum_52305_runcore_01', ); foreach($tests as $test) { require_once './' . $test . '.php'; } $ips = array( '11.11.11.11', '22.22.22.22', '33.33.33.33', '11.11.11.11', '44.44.44.44', '33.33.33.33', '11.11.11.1', '22.22.22.2', '11.11.11.11', ); foreach($tests as $test) { unlink($test::FILE); echo "\n\n=============================\n{$test}\n"; foreach ($ips as $ip) { $test::inc($ip); var_dump(file_get_contents($test::FILE)); } echo "\n\n=============================\n\n"; }] выводы моих скриптов. их будем брат за "эталон". eol это просто модификация где константа не в начале добавляемой строки а в конце согласно замечанию runcore. Код (Text): ============================= ru_php_forum_52305_preg_replace_callback_03_02 IP=11.11.11.11 --- string(16) " 11.11.11.11 = 1" --- IP=22.22.22.22 --- string(32) " 11.11.11.11 = 1 22.22.22.22 = 1" --- IP=33.33.33.33 --- string(48) " 11.11.11.11 = 1 22.22.22.22 = 1 33.33.33.33 = 1" --- IP=11.11.11.11 --- string(48) " 11.11.11.11 = 2 22.22.22.22 = 1 33.33.33.33 = 1" --- IP=44.44.44.44 --- string(64) " 11.11.11.11 = 2 22.22.22.22 = 1 33.33.33.33 = 1 44.44.44.44 = 1" --- IP=33.33.33.33 --- string(64) " 11.11.11.11 = 2 22.22.22.22 = 1 33.33.33.33 = 2 44.44.44.44 = 1" --- IP=11.11.11.1 --- string(79) " 11.11.11.11 = 2 22.22.22.22 = 1 33.33.33.33 = 2 44.44.44.44 = 1 11.11.11.1 = 1" --- IP=22.22.22.2 --- string(94) " 11.11.11.11 = 2 22.22.22.22 = 1 33.33.33.33 = 2 44.44.44.44 = 1 11.11.11.1 = 1 22.22.22.2 = 1" --- IP=11.11.11.11 --- string(94) " 11.11.11.11 = 3 22.22.22.22 = 1 33.33.33.33 = 2 44.44.44.44 = 1 11.11.11.1 = 1 22.22.22.2 = 1" --- ============================= ============================= ru_php_forum_52305_preg_replace_callback_03_02_eol IP=11.11.11.11 --- string(16) "11.11.11.11 = 1 " --- IP=22.22.22.22 --- string(32) "11.11.11.11 = 1 22.22.22.22 = 1 " --- IP=33.33.33.33 --- string(48) "11.11.11.11 = 1 22.22.22.22 = 1 33.33.33.33 = 1 " --- IP=11.11.11.11 --- string(48) "11.11.11.11 = 2 22.22.22.22 = 1 33.33.33.33 = 1 " --- IP=44.44.44.44 --- string(64) "11.11.11.11 = 2 22.22.22.22 = 1 33.33.33.33 = 1 44.44.44.44 = 1 " --- IP=33.33.33.33 --- string(64) "11.11.11.11 = 2 22.22.22.22 = 1 33.33.33.33 = 2 44.44.44.44 = 1 " --- IP=11.11.11.1 --- string(79) "11.11.11.11 = 2 22.22.22.22 = 1 33.33.33.33 = 2 44.44.44.44 = 1 11.11.11.1 = 1 " --- IP=22.22.22.2 --- string(94) "11.11.11.11 = 2 22.22.22.22 = 1 33.33.33.33 = 2 44.44.44.44 = 1 11.11.11.1 = 1 22.22.22.2 = 1 " --- IP=11.11.11.11 --- string(94) "11.11.11.11 = 3 22.22.22.22 = 1 33.33.33.33 = 2 44.44.44.44 = 1 11.11.11.1 = 1 22.22.22.2 = 1 " --- ============================= есть претензии к показателям? вроде корректно и добавлено и учтено. а вот вывод сценария runcore я отдельно вынес. во-первых прошу обратить внимание на замечание пхп-машины - всё на самом деле ок. поскольку я тестирую свою версию с блокировкой файла - она создает пустой файл при выполнении fopen(), а версия runcore просто пытается прочитать файл который мы удаляем перед тестом. во-вторых прошу обратит внимание как ломается статистика счетчиков. а вот это уже критично. Код (Text): ============================= ru_php_forum_52305_runcore_01 IP=11.11.11.11 --- PHP Warning: file_get_contents(/var/tmp/ramdisk/ru_php_forum_52305.txt): failed to open stream: No such file or directory in /lab/ru_php_forum/52305/ru_php_forum_52305_runcore_01.php on line 4 string(16) "11.11.11.11 = 1 " --- IP=22.22.22.22 --- string(32) "11.11.11.11 = 1 22.22.22.22 = 1 " --- IP=33.33.33.33 --- string(48) "11.11.11.11 = 1 22.22.22.22 = 1 33.33.33.33 = 1 " --- IP=11.11.11.11 --- string(48) "11.11.11.11 = 2 22.22.22.22 = 1 33.33.33.33 = 1 " --- IP=44.44.44.44 --- string(64) "11.11.11.11 = 2 22.22.22.22 = 1 33.33.33.33 = 1 44.44.44.44 = 1 " --- IP=33.33.33.33 --- string(64) "11.11.11.11 = 2 22.22.22.22 = 1 33.33.33.33 = 2 44.44.44.44 = 1 " --- IP=11.11.11.1 --- string(64) "11.11.11.11 =32 22.22.22.22 = 1 33.33.33.33 = 2 44.44.44.44 = 1 " --- IP=22.22.22.2 --- string(64) "11.11.11.11 =32 22.22.22.22 =21 33.33.33.33 = 2 44.44.44.44 = 1 " --- IP=11.11.11.11 --- string(64) "11.11.11.11 =33 22.22.22.22 =21 33.33.33.33 = 2 44.44.44.44 = 1 " --- ============================= Добавлено спустя 35 минут 56 секунд: кстати, runcore, ты используешь LOCK_EX при записи в файл. а ты не тестировал что будет если файл уже заблокирован? я спрашиваю к тому что мой говнокод блокировки файла годами проверен на моих пхп-демонах и он именно ждет блокировки (положенное кол-во итераций) и если блокировки не случилось - в данном случае завершает сценарий. а что будет с file_put_contents() будет ждать или просто тупо не запишет? опять же я блокирую файл на всё время чтения-обработки-записи дабы счетчики не изменились между чтением и записью. мне кажется твой код подвержен фейлу по состоянию гонки... Добавлено спустя 7 минут 14 секунд: я понял откуда это. в алгоритме-который-не-писал-denis01 я заменил $out = $out.PHP_EOL.$x[0].':'.$x[1]; на $out .= PHP_EOL . $x[0].'= '.$x[1]; а уже потом написал свою реализацию strstr_01 и потом уже были все регулярки. короче это копипаст кусочка кода denis01 который успешно прошел через все мои алгоритмы. но как мы понимаем - на логику оно вообще не влияет. подумаешь пустая строка в начале файла. сотри её и она там больше не появится.
я про тот который ты привел последним. в теме. я его процитировал. как еще точнее указать ? ты же сам его привел в своем сообщении. Добавлено спустя 11 минут 21 секунду: про то что ломается статистика. завтра гляну почему ломается мой вариант. а твой последний результат кода какой? твоего алгоритма.
наверное надо было отдельно уточнить. у меня упор на preg_replace_callback версий 02_01 - базовая, 03_01 - базовая с блокировкой, 03_02 - базовая с блокировкой и пробелами вокруг равно. hex-версии работают но файл при них требует утилит для конвертирования айпишника. поэтому они тут чисто для примера. остальные - могут не работать так как мне лениво было вдумчиво писать код либо я в отчете уже признал их недееспособность. все коды опубликованы. версия 03_02_eol это 03_02 только PHP_EOL в конец строки перемещен. 04_fix это версия 02_02 с измененной регуляркой - без ^$m. 02_02 в свою очередь просто вариант 02_01 с пробелами вокруг равенства. не стал её включать в результаты так как есть версия 03_02 - с пробелами и файловой блокировкой - тяжелее и интереснее. 04_fix_flock это 03_02 соответственно регуляркой без ^$m. то есть "fix" тут обозначает что "фиксированный" формат с пробелами вокруг равно.
если статистика очищается каждый день, возможно лучше будет, если создать файл в отдельной директорий для каждого нового айпи и в качестве имени файла выбрать айпи. Далее код с минимальной проверкой. Код (PHP): <?php if(!is_dir('iplist/')) mkdir('iplist/'); $f='iplist/'.getenv('REMOTE_ADDR'); //директория/имя_файла //exists -r -w is_file($f)&&is_readable($f)&&is_writable($f) ? file_put_contents($f, file_get_contents($f)+1) : file_put_contents($f, 1);
об этом ничего не сказано в оригинальной задаче, но думаю это слишком сильная девиация. к тому же можно провести простой тест. создаем 1000000 записей в файле. при 15 байтах на адрес + допустим 13 байтах на запись счетчика получаем примерно 28мб файл. этот файл легко открывается любым текстовым редактором. можно искать адрес, можно искать значение. теперь создаем 1млн файлов в каталоге. и пробуем хотя бы войти в каталог. сложно? а то ж. далее. поиск адреса это по сути поиск нода, значит тут всё от файловой системы будет зависеть. а вот поиск по значениям уже требует перечитки каждого файла. а это одинаково накладно для всех фс. еще минусы: * ноды в файловой системе не безлимитны. у меня например в среднем 10млн на раздел. даже если весь раздел будет отдан этой реализации - она сможет в себе уместить только примерно 10млн адресов. из 4млрд. а вот файлик в 100-120 гб вполне можно разместить. * в каждом файле мы будем хранить короткое строчное представление числового значения счетчика. напомню, 10 байт хватает для записи 10млрд. но сам файл будет выравнен под форматирование жесткого диска. например в старые добрые 512 байт. плюс запись о файле в метафайловой таблице. то есть оверхед на хранение короткого числа вообще жуткий. так что не особо важно как часто будут производиться действия (а в задаче вообще ничего ни о чем кроме инкремента счетчиков не было сказано) - файл на адрес это имхо не лучшая реализация.
убрал баг указанный Ганзалом. и блокировку, по ней нужно отдельно тестить. спорить не буду. в исходном задании об этом ничего не было, поэтому можно на этом не заострять внимание пока. главное сам подход к решению. а проблема блокировки или будет у всех или ни у кого. Код (PHP): class ru_php_forum_52305_runcore_01 extends ru_php_forum_52305 { public static function inc ($ip) { $s = file_get_contents(static::FILE); $ip = ' '.$ip.' = '; if ( ($p = strpos($s, $ip)) !== false ) { $p_cnt = $p +strlen($ip); $p_nl = strpos($s, PHP_EOL, $p_cnt); $cnt = intval( $p_nl!==false ? substr($s, $p_cnt, $p_nl-$p_cnt) : substr($s, $p_cnt) ); file_put_contents(static::FILE, substr_replace($s, $cnt+1, $p_cnt, strlen($cnt)) ); } else { // new ip file_put_contents(static::FILE, $ip.'1'.PHP_EOL, FILE_APPEND ); } } } Добавлено спустя 30 минут 21 секунду: насчет твоего кода.ХЗ если честно. скопировал твой вариант. проверил. глючит. Код (PHP): define('FILE', './ip.txt'); touch(FILE); // function inc_ganzal($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(FILE)); if (!$done) { $f .= PHP_EOL . $ip . '=1'; } file_put_contents(FILE, $f); } // $ips = [ '11.11.11.11', '22.22.22.22', '33.33.33.33', '11.11.11.11', '44.44.44.44', '33.33.33.33', '11.11.11.1', '22.22.22.2', '11.11.11.11', ]; foreach($ips as $ip) { inc_ganzal($ip); } echo '<pre>'; print_r( file_get_contents(FILE) ); unlink(FILE); выводит PHP Version 5.4.32 PCRE (Perl Compatible Regular Expressions) Support enabled PCRE Library Version 8.32 2012-11-30 ААУУ. народ. затестите у себя кто нить еще, код что выше. у кого что выдаст?
хм... тестовый сервер (на котором всё это писалось-запускалось) и один произвольно взятый боевой сервер: Код (Text): 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 PCRE (Perl Compatible Regular Expressions) Support => enabled PCRE Library Version => 8.31 2012-07-06 Код (Text): $ php runcore.php <pre> 11.11.11.11=3 22.22.22.22=1 33.33.33.33=2 44.44.44.44=1 11.11.11.1=1 22.22.22.2=1 нашел пых на винде: Код (Text): PHP 5.5.18 (cli) (built: Oct 15 2014 13:05:29) Copyright (c) 1997-2014 The PHP Group Zend Engine v2.5.0, Copyright (c) 1998-2014 Zend Technologies with Xdebug v2.2.7, Copyright (c) 2002-2015, by Derick Rethans PCRE (Perl Compatible Regular Expressions) Support => enabled PCRE Library Version => 8.34 2013-12-15 Код (Text): $ php runcore.php <pre> 11.11.11.11=1 22.22.22.22=1 33.33.33.33=1 11.11.11.11=1 44.44.44.44=1 33.33.33.33=1 11.11.11.1=1 22.22.22.2=1 11.11.11.11=1 наличие странного поведения признаю. пойду подумаю что может быть не так.
ну и вполне очевидно почему так происходит: Код (PHP): <?php echo PHP_OS, "\n"; $ips = [ '11.11.11.11' => 'INC', '22.22.22.22' => 'INC', '33.33.33.33' => 'INC', '11.11.11.11' => 'INC', '44.44.44.44' => 'ADD', '33.33.33.33' => 'INC', '11.11.11.1' => 'INC', '22.22.22.2' => 'ADD', '11.11.11.11' => 'INC', ]; $base = [ '11.11.11.11', '22.22.22.22', '33.33.33.33', '11.11.11.1', '11.11.11.11', ]; $tests = [ 'EOL' => 'PHP_EOL', 'lf' => '\n', 'crlf' => '\r\n', 'cr' => '\r', ]; $test_EOL = implode(PHP_EOL, $base); $test_lf = implode("\n", $base); $test_crlf = implode("\r\n", $base); $test_cr = implode("\r", $base); foreach ($tests as $name => $eol) { echo "\nTEST {$name}, EOL={$eol}\n"; $test = ${'test_' . $name}; foreach ($ips as $ip => $expected) { $action = (preg_match('~^'.preg_quote($ip).'$~m', $test) ? 'INC' : 'ADD'); echo sprintf("IP = %15s EXP = %s ACT = %s RESULT : %s\n", $ip, $expected, $action, ($expected==$action ? 'ok' : 'FAIL')); } } lin Код (Text): Linux TEST EOL, EOL=PHP_EOL IP = 11.11.11.11 EXP = INC ACT = INC RESULT : ok IP = 22.22.22.22 EXP = INC ACT = INC RESULT : ok IP = 33.33.33.33 EXP = INC ACT = INC RESULT : ok IP = 44.44.44.44 EXP = ADD ACT = ADD RESULT : ok IP = 11.11.11.1 EXP = INC ACT = INC RESULT : ok IP = 22.22.22.2 EXP = ADD ACT = ADD RESULT : ok TEST lf, EOL=\n IP = 11.11.11.11 EXP = INC ACT = INC RESULT : ok IP = 22.22.22.22 EXP = INC ACT = INC RESULT : ok IP = 33.33.33.33 EXP = INC ACT = INC RESULT : ok IP = 44.44.44.44 EXP = ADD ACT = ADD RESULT : ok IP = 11.11.11.1 EXP = INC ACT = INC RESULT : ok IP = 22.22.22.2 EXP = ADD ACT = ADD RESULT : ok TEST crlf, EOL=\r\n IP = 11.11.11.11 EXP = INC ACT = INC RESULT : ok IP = 22.22.22.22 EXP = INC ACT = ADD RESULT : FAIL IP = 33.33.33.33 EXP = INC ACT = ADD RESULT : FAIL IP = 44.44.44.44 EXP = ADD ACT = ADD RESULT : ok IP = 11.11.11.1 EXP = INC ACT = ADD RESULT : FAIL IP = 22.22.22.2 EXP = ADD ACT = ADD RESULT : ok TEST cr, EOL=\r IP = 11.11.11.11 EXP = INC ACT = ADD RESULT : FAIL IP = 22.22.22.22 EXP = INC ACT = ADD RESULT : FAIL IP = 33.33.33.33 EXP = INC ACT = ADD RESULT : FAIL IP = 44.44.44.44 EXP = ADD ACT = ADD RESULT : ok IP = 11.11.11.1 EXP = INC ACT = ADD RESULT : FAIL IP = 22.22.22.2 EXP = ADD ACT = ADD RESULT : ok win Код (Text): WINNT TEST EOL, EOL=PHP_EOL IP = 11.11.11.11 EXP = INC ACT = INC RESULT : ok IP = 22.22.22.22 EXP = INC ACT = ADD RESULT : FAIL IP = 33.33.33.33 EXP = INC ACT = ADD RESULT : FAIL IP = 44.44.44.44 EXP = ADD ACT = ADD RESULT : ok IP = 11.11.11.1 EXP = INC ACT = ADD RESULT : FAIL IP = 22.22.22.2 EXP = ADD ACT = ADD RESULT : ok TEST lf, EOL=\n IP = 11.11.11.11 EXP = INC ACT = INC RESULT : ok IP = 22.22.22.22 EXP = INC ACT = INC RESULT : ok IP = 33.33.33.33 EXP = INC ACT = INC RESULT : ok IP = 44.44.44.44 EXP = ADD ACT = ADD RESULT : ok IP = 11.11.11.1 EXP = INC ACT = INC RESULT : ok IP = 22.22.22.2 EXP = ADD ACT = ADD RESULT : ok TEST crlf, EOL=\r\n IP = 11.11.11.11 EXP = INC ACT = INC RESULT : ok IP = 22.22.22.22 EXP = INC ACT = ADD RESULT : FAIL IP = 33.33.33.33 EXP = INC ACT = ADD RESULT : FAIL IP = 44.44.44.44 EXP = ADD ACT = ADD RESULT : ok IP = 11.11.11.1 EXP = INC ACT = ADD RESULT : FAIL IP = 22.22.22.2 EXP = ADD ACT = ADD RESULT : ok TEST cr, EOL=\r IP = 11.11.11.11 EXP = INC ACT = ADD RESULT : FAIL IP = 22.22.22.22 EXP = INC ACT = ADD RESULT : FAIL IP = 33.33.33.33 EXP = INC ACT = ADD RESULT : FAIL IP = 44.44.44.44 EXP = ADD ACT = ADD RESULT : ok IP = 11.11.11.1 EXP = INC ACT = ADD RESULT : FAIL IP = 22.22.22.2 EXP = ADD ACT = ADD RESULT : ok Добавлено спустя 38 минут 9 секунд: тихо беседу сам с собою веду... лечится так: --- $action = (preg_match('~^'.preg_quote($ip).'$~m', $test) ? 'INC' : 'ADD'); +++ $action = (preg_match('~^'.preg_quote($ip).'\s*$~m', $test) ? 'INC' : 'ADD'); lin Код (Text): Linux TEST EOL, EOL=PHP_EOL IP = 11.11.11.11 EXP = INC ACT = INC RESULT : ok IP = 22.22.22.22 EXP = INC ACT = INC RESULT : ok IP = 33.33.33.33 EXP = INC ACT = INC RESULT : ok IP = 44.44.44.44 EXP = ADD ACT = ADD RESULT : ok IP = 11.11.11.1 EXP = INC ACT = INC RESULT : ok IP = 22.22.22.2 EXP = ADD ACT = ADD RESULT : ok TEST lf, EOL=\n IP = 11.11.11.11 EXP = INC ACT = INC RESULT : ok IP = 22.22.22.22 EXP = INC ACT = INC RESULT : ok IP = 33.33.33.33 EXP = INC ACT = INC RESULT : ok IP = 44.44.44.44 EXP = ADD ACT = ADD RESULT : ok IP = 11.11.11.1 EXP = INC ACT = INC RESULT : ok IP = 22.22.22.2 EXP = ADD ACT = ADD RESULT : ok TEST crlf, EOL=\r\n IP = 11.11.11.11 EXP = INC ACT = INC RESULT : ok IP = 22.22.22.22 EXP = INC ACT = INC RESULT : ok IP = 33.33.33.33 EXP = INC ACT = INC RESULT : ok IP = 44.44.44.44 EXP = ADD ACT = ADD RESULT : ok IP = 11.11.11.1 EXP = INC ACT = INC RESULT : ok IP = 22.22.22.2 EXP = ADD ACT = ADD RESULT : ok TEST cr, EOL=\r IP = 11.11.11.11 EXP = INC ACT = ADD RESULT : FAIL IP = 22.22.22.22 EXP = INC ACT = ADD RESULT : FAIL IP = 33.33.33.33 EXP = INC ACT = ADD RESULT : FAIL IP = 44.44.44.44 EXP = ADD ACT = ADD RESULT : ok IP = 11.11.11.1 EXP = INC ACT = ADD RESULT : FAIL IP = 22.22.22.2 EXP = ADD ACT = ADD RESULT : ok win Код (Text): WINNT TEST EOL, EOL=PHP_EOL IP = 11.11.11.11 EXP = INC ACT = INC RESULT : ok IP = 22.22.22.22 EXP = INC ACT = INC RESULT : ok IP = 33.33.33.33 EXP = INC ACT = INC RESULT : ok IP = 44.44.44.44 EXP = ADD ACT = ADD RESULT : ok IP = 11.11.11.1 EXP = INC ACT = INC RESULT : ok IP = 22.22.22.2 EXP = ADD ACT = ADD RESULT : ok TEST lf, EOL=\n IP = 11.11.11.11 EXP = INC ACT = INC RESULT : ok IP = 22.22.22.22 EXP = INC ACT = INC RESULT : ok IP = 33.33.33.33 EXP = INC ACT = INC RESULT : ok IP = 44.44.44.44 EXP = ADD ACT = ADD RESULT : ok IP = 11.11.11.1 EXP = INC ACT = INC RESULT : ok IP = 22.22.22.2 EXP = ADD ACT = ADD RESULT : ok TEST crlf, EOL=\r\n IP = 11.11.11.11 EXP = INC ACT = INC RESULT : ok IP = 22.22.22.22 EXP = INC ACT = INC RESULT : ok IP = 33.33.33.33 EXP = INC ACT = INC RESULT : ok IP = 44.44.44.44 EXP = ADD ACT = ADD RESULT : ok IP = 11.11.11.1 EXP = INC ACT = INC RESULT : ok IP = 22.22.22.2 EXP = ADD ACT = ADD RESULT : ok TEST cr, EOL=\r IP = 11.11.11.11 EXP = INC ACT = ADD RESULT : FAIL IP = 22.22.22.22 EXP = INC ACT = ADD RESULT : FAIL IP = 33.33.33.33 EXP = INC ACT = ADD RESULT : FAIL IP = 44.44.44.44 EXP = ADD ACT = ADD RESULT : ok IP = 11.11.11.1 EXP = INC ACT = ADD RESULT : FAIL IP = 22.22.22.2 EXP = ADD ACT = ADD RESULT : ok маководов даже и не собираюсь ублажать, пардон. Добавлено спустя 5 минут 1 секунду: блин, я там в $base случайно 11,11,11,11 продублировал... а регулярку хочу вот такую Код (Text): ~^'.preg_quote($ip).'\s*=\s*(\d+)\D*$~m Добавлено спустя 31 минуту 18 секунд: ну и безусловно поздравления новому лидеру теста: Код (Text): BANK : 0 CNT : 9 UNQ : 3 ROUNDS : 10000 BASE : INIT ru_php_forum_52305_preg_replace_callback_03_01 : 4.247025 0.000047 593680 ru_php_forum_52305_preg_replace_callback_03_02 : 4.210094 0.000047 594448 ru_php_forum_52305_preg_replace_callback_05_01 : 4.234770 0.000047 594864 ru_php_forum_52305_preg_replace_callback_05_02 : 4.241270 0.000047 595408 ru_php_forum_52305_preg_replace_callback_05_02_noflock : 3.134184 0.000035 595408 ru_php_forum_52305_runcore_01 : 2.582040 0.000029 595408 ru_php_forum_52305_runcore_02 : 2.490332 0.000028 595432 BANK : 1 CNT : 117 UNQ : 5 ROUNDS : 1000 BASE : INIT ru_php_forum_52305_preg_replace_callback_03_01 : 5.526047 0.000047 613136 ru_php_forum_52305_preg_replace_callback_03_02 : 5.513646 0.000047 613136 ru_php_forum_52305_preg_replace_callback_05_01 : 5.552962 0.000047 613256 ru_php_forum_52305_preg_replace_callback_05_02 : 5.534556 0.000047 613256 ru_php_forum_52305_preg_replace_callback_05_02_noflock : 4.106169 0.000035 613256 ru_php_forum_52305_runcore_01 : 3.368238 0.000029 613256 ru_php_forum_52305_runcore_02 : 3.202887 0.000027 613256 BANK : 2 CNT : 123 UNQ : 10 ROUNDS : 1000 BASE : INIT ru_php_forum_52305_preg_replace_callback_03_01 : 5.922501 0.000048 623976 ru_php_forum_52305_preg_replace_callback_03_02 : 5.881683 0.000048 623976 ru_php_forum_52305_preg_replace_callback_05_01 : 5.925225 0.000048 623976 ru_php_forum_52305_preg_replace_callback_05_02 : 5.924031 0.000048 623976 ru_php_forum_52305_preg_replace_callback_05_02_noflock : 4.437509 0.000036 623976 ru_php_forum_52305_runcore_01 : 3.535712 0.000029 623976 ru_php_forum_52305_runcore_02 : 3.422978 0.000028 623976 BANK : g0 CNT : 10000 UNQ : 217 ROUNDS : 5 BASE : INIT ru_php_forum_52305_preg_replace_callback_03_01 : 3.876294 0.000078 3367736 ru_php_forum_52305_preg_replace_callback_03_02 : 3.914231 0.000078 3367736 ru_php_forum_52305_preg_replace_callback_05_01 : 3.967946 0.000079 3367736 ru_php_forum_52305_preg_replace_callback_05_02 : 4.004731 0.000080 3367736 ru_php_forum_52305_preg_replace_callback_05_02_noflock : 3.378140 0.000068 3367736 ru_php_forum_52305_runcore_01 : 1.565151 0.000031 3367736 ru_php_forum_52305_runcore_02 : 1.512397 0.000030 3367736 BANK : g1 CNT : 10000 UNQ : 1488 ROUNDS : 5 BASE : INIT ru_php_forum_52305_preg_replace_callback_03_01 : 12.936603 0.000259 4841752 ru_php_forum_52305_preg_replace_callback_03_02 : 13.319694 0.000266 4841752 ru_php_forum_52305_preg_replace_callback_05_01 : 12.973185 0.000259 4841752 ru_php_forum_52305_preg_replace_callback_05_02 : 13.375181 0.000268 4841752 ru_php_forum_52305_preg_replace_callback_05_02_noflock : 12.793856 0.000256 4841752 ru_php_forum_52305_runcore_01 : 2.913258 0.000058 4841752 ru_php_forum_52305_runcore_02 : 3.102064 0.000062 4841752 BANK : g2 CNT : 10000 UNQ : 6921 ROUNDS : 5 BASE : INIT ru_php_forum_52305_preg_replace_callback_03_01 : 47.169284 0.000943 4842912 ru_php_forum_52305_preg_replace_callback_03_02 : 63.810053 0.001276 4842912 ru_php_forum_52305_preg_replace_callback_05_01 : 70.986210 0.001420 4842912 ru_php_forum_52305_preg_replace_callback_05_02 : 73.925443 0.001479 4842912 ru_php_forum_52305_preg_replace_callback_05_02_noflock : 72.972932 0.001459 4842912 ru_php_forum_52305_runcore_01 : 14.079614 0.000282 4842912 ru_php_forum_52305_runcore_02 : 19.455407 0.000389 4842912 где мои 05 это с обновленной регуляркой, _01 произвольные пробелы, _02 фиксированные пробелы, runcore_01 первый алгоритм который провалился, _02 - второй, который дает почти актуальные данные (почти потому что метод инициализации подсовывает другой формат окончания строки. только и всего), но однозначно показывает уверенно быстрый результат. поскольку в нем нет блокировки - preg_replace_callback_05_02_noflock тоже без блокировки для сравнения. жаль я поленился написать итерацию с офсетом =)
молодец, разобрался где собака порылась) по сути, мой вариант, это та же твоя регулярка, тока сделанная вручную через строковые функции. профит от этого есть небольшой, зато это трудоемко, медленно пишется и чревато многими ошибками. регулярки, как видно, работают ОЧЕНЬ шустро, быстро пишутся и достаточно наглядны.
Вместо use() $c Код (PHP): <?php $ip = getenv('REMOTE_ADDR') ; $f = 'ip.txt' ; $p_r_c = preg_replace_callback( '/(?<=\b'.$ip.')(\h*=\h*)(\d{1,})\b/' , function($m){ return trim($m[1]) . ++$m[2] ; } , file_get_contents($f), 1, $c ) ; $c > 0 ? file_put_contents($f, $p_r_c) : file_put_contents($f, $ip.'=1' . PHP_EOL, FILE_APPEND) ; ?>