Pull to refresh

Comments 192

К своему стыду ни разу не задавался вопросами подобной оптимизации и был приятно удивлен скоростью парсинга INI-файлов! Спасибо за сравнения, заставили задуматься :)
Ну правильно, в случае парсинга ini файлов или же json'а вызываются Cшные функции, они всяко быстрее реализаций на чистом php
Если считаете, что уже достаточно овладели PHP, поизучайте язык более низкого уровня (C / Pascal), чтобы понимать как оно работает и подобными вещами вас будет уже не удивить ;)
Ну Си — да, а вот паскаль вряд ли поможет (:
Pascal may be an admirable language for teaching beginners how to program

Это сказал некто Керниган.

Некто Дональд Кнут в 2000 году сам портировал TeX на Pascal — как думаете, с чего бы?

Не нужно оголтело равнять Pascal = Delphi’s VCL и делать поспешные выводы.
Для обучения новичков программированию Паскаль хорош. Потому что начинать с Си очень трудно. Но для того, чтобы понимать как работает PHP на более низком уровне знания Паскаля вряд ли помогут.
В цитате главное слово — «how».

Вы хотите поговорить, как работает PHP на низком уровне? — Тогда нужно сначала попрограммировать на C/C++, а потом заглядывать в код функций PHP (а не программировать на PHP непосредственно).

Хорошая абстракция языка всегда подразумевает, что хорошему программисту на этом языке не нужно знать тонкости реализации. Это правильно, что p00h никогда об этом не задумывался. Об этом не нужно задумываться. По хорошему, я бы хотел увидеть пару функций store_config/load_config, и не задаваться вопросом, какой из стапятисот предложенных авторами языка методов мне выбрать.

Если вернуться к паскалю — гляньте на оператор «^». Или вообще на спецификацию посмотрите. А то на «низком» уровне все программировать научились, а смысл термина как-то стерся.
функции store_config/load_config — это уже не уровень языка, это более высокий уровень абстракции — фреймворки, etc, потому что config — это само по себе абстрактное понятие в отличие от xml, ini или что там еще.
А насчет паскаля — насколько я его помню, указатели в нем не настолько «продвинуты», как в Си. Поправьте меня, если я не прав.
ага, более. но никто не мешает делать все базовые вещи с указателями и в паскале. хотя конечно после С паскаль вспоминаешь как тихий ужас в этом плане.
а указатели на функции в нем есть? гугл так и не дал внятного ответа.
честно говоря лет 10 минимум уже не прикасался к паскалю. и тогда когда использовал, то не чистый паскаль был, а «дельфи». там была возможность.
Одна из первых заповедей оптимизации — используй native функции (если такая есть конечно). Они всегда быстрее. Альшанецкий говорил что в его практике только один раз кастомное решение на чистом PHP было быстрее нативной функции, а это о чем-то да говорит )
Говорит о том, что нативная функция кривая, очевидно же.
Стоит заметить, что внешние файлы с настройками должны быть в закрытой папке, либо на уровень выше www.
В случае конфигов на PHP это не обязательно. Даже если злоумышленник найдет config.php и откроет его через браузер, он увидит пустую страничку.
Кроме этого, доступ к конфигам можно ограничивать через настройки сервера (как вариант, .htaccess).
Но, в сущности, Вы правы — защищать конфиги — первое дело каждого программиста.
>В случае конфигов на PHP это не обязательно.

все так говорят, пока глюканувший сервер не начнет отдавать исходники скриптов :)
Когда начал читать, то тоже подумал, что INI будут хуже обычного PHP-файла с массивом. А оно вон как повернулось.
Прочитав статью меня чуть кондрашка не схватила…
Чтение сериализованного массива из файла и его последующая ансериализация быстрее чтения переменных из php-файла…

Сериализованные массивы в файлах эффективны, если ими можно заменить данные из БД.
Ваша кондрашка умеет читать? ;)
UFO just landed and posted this here
Нет, мимо станции проезжать умеет))
Мимо станции умеет проезжать Шляпа™ им. Чехова, вы что-то путаете :-)
Парсить сериализованный массив менее ресурсоёмко, чем парсить php-код. Всё верно.
А на доступ к файлу смотреть не нужно, так как он имеет место в обоих случаях.
Но если php-код уже пропарсен php-акселератором, тогда его просто нужно выполнить.
Жаль не проверяли
Самое интересное, что всю жизнь считал, что php-код будет как раз-таки быстрее, и кешировал ini-файлы в php-код. Теперь-то буду знать, что эти усилия никому, оказывается, не нужны.
Настройки лучше кешировать в памяти.
А сколько раз проводился каждый тест?
1000 последовательных запусков для каждого из алгоритмов.
Все тесты вместе перезапускал несколько десятков раз, чтобы проверить, что загрузка сервера не влияет на результаты. Результаты по времени между запусками отличались на незначимую величину :-)
Эцсамое, а нельзя подгрузить весь «тяжёлый» хлам, а потом схоронить где-нибудь в memcache в сериализованном виде?
Тоже самое подумал. Хранить конфиги надо там где удобно (база, ini или xml файлы, главное не define'ами ;)), и при проблемах с производительностью просто кэшировать их. Хоть в мемкэш, хоть даже в файлы в сериализованном виде. Будет и быстро и удобно.
а чем так плохи defin'ы, можно поподробней?
Навскидку: глобальное пространство имен, невозможность переопределить.
Если в двух словах, то:
  • Они медленные
  • Они глобальные. Если переменные можно сделать локальными (включить определение переменных в теле функции, например), то константы — нет
  • Они ужасные: include(PROJECT_ROOT. "/lib/class.php") vs include("$project_root/lib/class.php")
насчет медленных согласен, внешний вид это субъективно, а вот глобальность — почему это плохо?
если речь идет о глобальном конфиге сайта
почему это хуже чем например делать
$cfg = config::getInstance();
в каждом месте где нужно узнать project_root?

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

config->switchInstance('additional');
runMethod();
config->switchInstance('main');

function runMethod(){
    $cfg = config::getInstance();
    ...
}
Вы в чем-то правы.
Но большинство реализаций с константами выглядят примерно так (схема главного скрипта):

include конфиг;
include классы x 10;
include коннект к базе данных;

константы при этом встречаются только в инклюдах в главном скрипте и в коннекте к базе данных. Вопрос: а смысл было их делать глобальными?

Хотя реализации, повторю, могут отличаться
Невозможность определять массивы, объекты.
У меня только один вопрос — настройки подключения к memcache где хранить? :-)
Логичнее всего сохранять такую инфу в apc. 0.002 0.015 0.17
Интересно, но обычными инклудами получилось 0.08 против 0.17
Есть над чем задуматься
Смысла в ваших цифрах нет, железо и тестовые данные у вас с автором разное.
Выкладывайте тогда результаты всех тестов (txt, ini, xml, ...).
Какая библиотека использовалась для кеширования опкода?
Важное уточнение. PHP 5.2.x + eAccelerator 0.9.6.x
Linux Debian Lenny

Если интересно, то
Intel Core2Duo 3000 GHz
Memory 4GB 800 MHz
2x Western Digital RE3 RAID1
Убедили :)
Просто если бы вы не использовали опкод кешер, или проводили тесты в Виндовсе, это сильно сказалось бы на времени подключения файлов.
Практический подход к оптимизации (на базе опытов).

Берем в руки микротайм и считаем в узловых точках скриптов время и считаем. В местах, где можно существенно сэкономить — экономим.

Я не могу сейчас уже привести примеры кода, но могу описать ситуацию, когда экономил на выводе больших массивов данных из базы. Удобно, например, при выводе детализаций по всякого рода аккаунтингу (трафик, звонки, смс и прочие тарифицируемые...). Такие данные характерны короткими справочниками с десятками записей и огромной таблицей CDR (собственно записей тарификации). Вот в таких ситуациях, чтение справочников целиком из базы в массив в памяти и использование этих массивов при выводе с одним простым запросом только к таблице CDR в базе, позволяется «разогнать» скрипт в сотни раз, по сравнению с решением в лоб — запросами с join-ами в базу данных.

Конечно, это скорее оптимизация СУБД, но средствами языка программирования. :)
А все-таки стоит проверить хранение конфигов в базе данных. Да, конфиги в базе данных сильно проигрывают в скорости на указанных вами условиях работы. Но стоит рассмотреть другие варианты: чтение 20 настроек при определенных 1000, сохранение настроек.
а хранить настройки подсоединения к БД? тоже в БД?
Настройки подсоединения к БД можно ужать до одной строки (e.g. 'mysql://user:password@host:port'), где её хранить — некритично.
А БД бывают разные, к примеру, SQLite, которому только путь до БД нужен по сути.
В ваших примерах используются разные функции для чтения файлов. Мне кажется это тоже повлияло на результат исследования.
Старался по минимуму использовать PHP-wrapper'ы для работы с файлами, только там, где это действительно необходимо. А на оптимизацию работы с файловой системой на уровне ядра PHP большинство людей повлиять не могут, поэтому она не оценивалась.
а я и не говорю что нужно влиять. Нужно было при замерах использовать одну и ту же функцию для чтения файлов, думаю результат будет отличным от Вашего.
Например, вместо include($file) использовать eval('?'.'>'.file_get_contents($file).'<'.'?')
Или Вы имели в виду, что нужно избавиться от fopen/fgets/fclose и заменить на foreach (explode('\n', file_get_contents($file)) as $line)?
С последним, пожалуй, соглашусь, разница, наверное, будет.
последнее Вы верно поняли.
Добавил пример с file_get_contents. Вопреки ожиданиям, разница не очень существенна, и, в основном, заметна для больших конфигов.
согласен с предыдущим оратором
и 5 копеек
$file_array = file($file);
foreach ($file_array as $line)
Для полноты картины можно еще проверить JSON, YAML
Хотя YAML должен быть достаточно медленным из-за отсутствия встроенных решений для парсинга, интереснее все же JSON.
Очень удобно хранить настройки в YAML, который спокойно и быстро читает и пишет свежая версия Zend Framework.
И благополучно конвертирует и кэширует их в pure php
UFO just landed and posted this here
Добавил JSON.
Методы парсинга данных средствами PHP, по идее, должны быть сравнимы с разбором текстовых файлов или даже медленнее.
Спасибо. Чтобы возвращало массив — можно второй параметр указать, но не думаю что на результаты это сильно повлияет
Спасибо, не учел.
В 99% случаев использую json_encode :-)
Провел тесты приведённых скриптов на своей машине и о чудо… Настройки хранимые в php-скриптах на 20% быстрее, чем файлы с сериализованными массивами. Совершенно противоположные результаты.

Ошибка автора:
— Не учтено кэширование файлов ФС и кэширование данных PHP. Так как оба метода хранения данных (php-скрипты и сериализованные массивы в файлах) относительно мало различаются по скорости, то «играясь» с настройками кэширования можно добиться совершенно разных результатов.
На практике данный пример может действовать как и в положительную сторону, так и в отрицательную.
Согласен, не учел эти моменты.
А у Вас какие настройки кэширования данных PHP? Имеется ли какой-либо оптимизатор?
«стандартный» (с версии php 6.0 — internal)
php_APC
для «крутатинушки-крутатастой»:
apc.stat = «Off»
ну а потом в «нужные моменты времени»
apc_clear_cache('opcode');
XML надо было парсить через SimpleXML, результат был бы лучше. (проверено на собственной шкуре)
И еще, если на сервер поставить xcache — то чтение php файла с конфигом будет происходить мгновенно из рама (без учета дисковой операции). И по теории будет быстрее.
НапИшите примерчик? Буду благодарен!
function config($file)
{
    return simplexml_load_file($file);
}
function config($file) {
    $xml = simplexml_load_file($file);
    $array = array();
    foreach($xml as $key => $value) {
        $array[$key] = (string)$value;
    }
    return $array;
}


Как-то так…
А xcache — это расширение для php
Насколько помню к SimpleXML Object можно обращаться как к массиву, потому ваш цикл for — излишен. Если тип данных критичен, то можно просто рекурсивно скастовать тип через array_walk_recursive.
*SimpleXMLElement, старческая память
В случае файла настроек, который будет преобразован в хеш-массив а DOM как таковой нафиг не нужен, нужно парсить потоковым SAX парсером.
Если стоит задача только читать конфиг из програмы (менять только ручками в редакторе), то быстрее будет делать через XMLReader. Особенно заметно на больших файлах.

Правда конфиг прийдется привести к другому виду:

1
2
3
Парсер рулит ))
Вместо элетментов x1, x2, x3, ставить элемент param с аттрибутом name = x1, x2, x3.
Вообще-то если начинаешь задумываться о скорости, имеет смысл держать все в памяти – и настройки, и общие данные. Но в этом случае PHP, естественно, исключен.
Все эти танцы с бубном опять лишь для красоты, результаты предопределены. Вопрос в том, что делать дальше? Если это большой проект, где конфигурирование происходит из различных точек, никто не будет это переменными, ini файлами (многоуровневы конфигурации). Остается XML и JSON. Чаще всего применяется первый, не смотря на свою избыточность. Но никто не будет оставлять сборку и считыванию конфигов в голов виде. Будут кешировать на диск, в память, в memcache в результате ваша проделанная работа так и не раскрывает вопрос. Это сродни — функции работают быстрее классов, но мы ведь не будем все строить без ООП?
Тестирование не совсем верное: нет гарантий что все использованные алгоритмы имеют линейную скорость работы и вдруг не завтормозят на десятке значений.
Правильно бы было сгенерировать конфиг файл длиной хотя бы несколько десятков строк: это будет ближе к реальной ситуации.
Вы разве не об этом говорите?
Конфигурационный файл содержит 10, 100 или 1000 конфигурационных параметров, представляющих собой короткие строки
Ай, зевнул. Именно этим и занимаются люди по утрам, верно? :))
Вот оно как всё… Никогда раньше особо не задумывался над конфигами.
Простите мою глупость, но терзает вопрос, – Что за магические три числа в ваших результатах? Я не придираюсь, а правда не понял…
UFO just landed and posted this here
Конфигурационный файл содержит 10, 100 или 1000 конфигурационных параметров, представляющих собой короткие строки. Соответственно 3 результата для каждого набора
Это называется экономия на спичках. (Говоря русским языком, всё это онанизм для мозгов, и не более.)

Как я обычно говорю «если программисту кажется что тормозит язык программирования, значит тормозит сам программист.»
В чем-то Вы правы.
Чтение конфигов, по сути, редко является узким местом. Есть и более «узкие» места в скриптах.
UFO just landed and posted this here
Спасибо!
Расскажите, пожалуйста, как Вы делаете диаграммы?
Я 90% времени в консоли, и, к стыду своему, не знаю, как это делается :-)
UFO just landed and posted this here
UFO just landed and posted this here
UFO just landed and posted this here
Жесть. Не?

Вот как бы реально действующий код…

$Config = Helly_Config::GetInstance(); //Синглтон отнаследованный от ObjectArray

$Config['ТО'] = 1;
$Config['СЁ'] = 2'
$Config['ПЯТОЕ'] = 5;
//…
$Config['СТОПЯТОЕ'] = 105;
$Config['ОЛОЛОТРОЛОЛО'] = $Config['ПЯТОЕ'] == 5 ? 'это пять' : 'ничего подобного';


+ Автозагрузчик классов движка.

Ну и в результате можно везде делать Helly_Config::Get('ТО'); Helly_Config::Get('СЁ'); Helly_Config::Get('ПЯТОЕ'); Helly_Config::Get('ДЕСЯТОЕ');

Счастлив и не парюсь пока. Вчера сайт (сидящий по соседству с ещё десятком сайтов) пережил две с половиной тысячи уникальных посетителей без каких-либо тормозов. Процессор Atom и оперативной памяти 2Gb (То есть обычный жиденький «сервер»). Акселератора нет.

ИТОГО:
Со Smarty, DbSimple (работает с мускулом) и кучей классов движка (каюсь, подгружаются лениво) без какого-либо кеширования страница генерируется в среднем за 0,1 сек.

Или я чего-то не понял… Честно, извините.
Ну и да. Попробуйте такое $Config['ОЛОЛОТРОЛОЛО'] = $Config['ПЯТОЕ'] == 5? 'это пять': 'ничего подобного'; в INI-файле сделать.

В ЖЖ наверное так за конфиги не гонят…
> Вчера сайт (сидящий по соседству с ещё десятком сайтов) пережил две с половиной тысячи уникальных посетителей без каких-либо тормозов. Процессор Atom и оперативной памяти 2Gb (То есть обычный жиденький «сервер»). Акселератора нет.

Это не показатель крутости у меня на атоме, 1гб, около 60-80 тыс уников и 500 тыс просмотров
И проблема не в Атоме, а в том что канал всего 10мбит =)

Данные примеры будут полезны, только при очень большом наплыве пользователей
Жуть! Многим до такого ого-го…
*Одел шляпу*
– Снимаю шляпу.
*Снял шляпу*

> Данные примеры будут полезны, только при очень большом наплыве пользователей

Ну почему у него в лучшем случае конфиг читается всего в два раза быстрее, чем у меня весь сайт работает (с известными шаблонизатором и БД)?!
А Вы какой конфиг имеете в виду — из 10 строк, 100 или 1000?
Вот такой:
<?php

$Config = Helly_Config::GetInstance();

// Site pathes:
$Config['ROOT_DIR'] = ROOT_DIR;
$Config['WEB_DIR'] = 'http://lastproof.ru';
$Config['COOKIE_PATH'] = '.lastproof.ru';

$Config['DIRS_RIGHTS'] = 0755;
$Config['FILES_RIGHTS'] = 0644;

// Metadata:
$Config['META_TITLE'] = 'Последнее доказательство';
$Config['META_DESCRIPTION'] = 'LastProof.Ru – генератор доказательств, непредвзято доказывающий что угодно.';
$Config['META_KEYWORDS'] = 'доказательство, proof, спор, холивар, генератор доказательств, правосудие, пари, аргумент, доказательства, lastproof.ru';

// Behaviour:
$Config['SITE_DEFAULT_LANG'] = 'ru';
$Config['SITE_PRODUCTION_MODE'] = false;
$Config['SITE_SKIN'] = 'default';
$Config['SITE_CLOSED'] = false;
$Config['INVITES_ENABLED'] = false;

// Basic directory:
$Config['HELLY_DIR'] = HELLY_DIR;

$Config['APPLICATION_DIR'] = APPLICATION_DIR;
$Config['APPLICATION_WEB_DIR'] = $Config['WEB_DIR']. '/app';

$Config['DATA_DIR'] = $Config['APPLICATION_DIR']. '/data';
$Config['LANGUAGES_DIR'] = $Config['DATA_DIR']. '/langs';
$Config['MODELS_DIR'] = $Config['APPLICATION_DIR']. '/models';
//$Config['PLUGINS_DIR'] = $Config['APPLICATION_DIR']. '/plugins';

$Config['SKINS_DIR'] = $Config['APPLICATION_DIR']. '/skins';
$Config['SKINS_WEB_DIR'] = $Config['APPLICATION_WEB_DIR']. '/skins';
$Config['SKIN_DIR'] = $Config['SKINS_DIR']. '/'. $Config['SITE_SKIN'];
$Config['SKIN_WEB_DIR'] = $Config['SKINS_WEB_DIR']. '/'. $Config['SITE_SKIN'];

$Config['PUBLIC_DIR'] = $Config['ROOT_DIR']. '/public';
$Config['PUBLIC_WEB_DIR'] = $Config['WEB_DIR']. '/public';

// Config for Helly_Router:
$Config['HELLY_ROUTER'] = array (
'allowed_controllers' => array (
'home' => 'Controller_Home',
'errors' => 'Controller_Errors',
'gates' => 'Controller_Gates',
'pages' => 'Controller_Pages',
'proof' => 'Controller_Proof',
),
'default_controller'=> 'home',
'errors_controller' => 'errors',
);

// Config for Helly_Viewer:
$Config['HELLY_VIEWER'] = array
(
'compile_check' => (false === $Config['SITE_PRODUCTION_MODE']? true: false),
'caching' => false,
'samples' => array(
'content' => array('messages'),
'sidebar' => array('empty'),
),
);

// Config for Helly_Database:
$Config['HELLY_DATABASE'] = array (
'db_configs' => array (
'default' => array (
'type' => 'mysql',
'host' => '***.**.***.***',
'port' => '****',
'user' => '**********',
'pass' => '**********',
'db_name' => '**********',
'prefix' => '****',
'charset' => 'utf8'
),
'xbt' => array (
'type' => 'mysql',
'host' => '***.**.***.***',
'port' => '****',
'user' => '*******',
'pass' => '********',
'db_name' => '*******',
'prefix' => '****',
'charset' => 'cp1251'
),
),
'log_errors' => true, // Log sql errors.
'log_queries' => true, // Log all sql queries.
);

// Config for Helly_Cacher:
$Config['HELLY_CACHER'] = array (
'cache_enabled' => true, // bool
'cache_type' => 'memory', // string[files/memory]

'cache_prefix' => Helly_UriManager::ClearUri($Config['WEB_DIR']), // Prefix for files of cache
'cache_dir' => $Config['DATA_DIR']. '/cache', // Directory for files of cache

'memcached_servers' => array( // Settings for connecting to memcached servers
array(
'host' => '*************',
'port' => *****,
'persistent'=> true
),
),
'memcached_compression' => true, // Use memcached compression
);

// Sending E-mails:
$Config['HELLY_MAILER'] = array (
'type' => 'smtp', // string[php/smtp]
'email' => '*******************',
'from' => $Config['META_TITLE'],
'smtp_host' => 'smtp.yandex.ru',
'smtp_port' => 25,
'smtp_user' => '*******************',
'smtp_password' => '********',
'smtp_auth' => true,
);

// Logging:
$Config['LOGS_DIR'] = $Config['DATA_DIR']. '/logs';
$Config['LOGS_FILE'] = 'helly.log';

?>

↑ это в оригинале 143 строки

И там ниже – што-што, простите? Не понял – вы для генерации одной страницы «считываете конфиг» более одного раза?
Ну есть же сервисы для обмена исходными текстами.
Ну есть же нормальные, менее депресовые роботы.
Возможно он использует FCGI… если вы свой сайт переведете на FCGI у вас тоже вырастит скорость…
Вот к примеру: ava.md/ — если без FCGI, тогда будет около 0.1-0.2сек, с FCGI 0.02 — и то 0.01 — тратится на PREG который ужимает HTML
время генерации можно посмотреть, правой клавишей, вторая строчка =)
У него растёт не скорость, а время выполнения. То есть всё очень МЕДЛЕННО. Или я чего-нибудь про единицы измерения не понял. Их разрядность…
> Ну почему у него в лучшем случае конфиг читается всего в два раза быстрее, чем у меня весь сайт работает (с известными шаблонизатором и БД)?!

Время считается для цикла из 1000 загрузок конфига. А у Вас генерируется 1 страница
UFO just landed and posted this here
UFO just landed and posted this here
Может быть автор диаграммы в топик перенесет?
Смею не согласится с автором.

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

Никто никогда не сомневался, парсинг XML в DOM это дорогая операция, но за удобство всегда нужно платить.

Откровенно говоря, все зависит от общей схемы работы и структуры.

Для меня удобнее хранить в XML (в атрибутах узлов я могу хранить что угодно и парсить данные на основе этого).
> астройки читаются один раз и доступны на протяжении работы сценария
А это как написать :)
Вы же не читаете файл конфига каждый раз при обращении за неким свойством, а первый раз читаете в массив к примеру.
Приходилось переписывать код, читающий настройки по одной из БД каждый раз, когда они становились нужны. Переписывал, и мысленно отрывал руки кодеру.
UFO just landed and posted this here
Где можно сделать запрос на Хабраэффект? :)
[a href=«yousite.com/»]сиськи здесь [/a] (В BB тегах потому что так надо :)
Зачем Вы положили yousite.com??? Они теперь страдают!
UFO just landed and posted this here
[img]http://yousite.com/[/img] Так даже про сиськи писать не нужно
Я сказал, что для оптимизации (обычно) есть более подходящие процессы.
интересно было бы узнать какими либами и их версиями проводилось считывание, а также резличие в одних и тех же форматах для различных поставленных вами «условий»… а то картинка не чёткая…
модераторы удалите топик, не позорьте ресурс
Ага, не понятно что делает технический топик на новостном и развлекательном ресурсе.
Может ваш оппонент так смеётся над полученными данными?
А я вообще ничего не понял, у меня и так все нормально работает.
чтобы JSON возвращал не stdClass object, а массив надо в функции json_decode 2 параметром дописать true
Опять экономят крохи не обращая внимания на слонов.
ini файлы — наиболее удобны в использовании, ими и надо пользоваться.
Когда как. Заглядите внутрь Мадженты и прикиньте как бы вы это делали ини-шками :)
В Мадженте масса «слонов» :)
Кстати при увеличении массива данных, json_decode себя покажет куда лучше чем unserialize.
У Вас есть весомые аргументы для этого?
Массив заполняется $size (amount of enrites в результатах) случайными строковыми значениями:
8-16 символов ord(rand(32, 128));
Затем сериализуется.
Время выполнения предлагается:
$seconds = sqrt($size / 10000);


Дальше идёт адаптивная схема измерения времени выполнения
$time1 = microtime(true);
$time2 = $time1;
$calls = 0;
while ($time2 <= $time1 + $seconds) {
	unserialize($string);
	$time2 = microtime(true);
	$calls++;
}

время выполнения одного вызова (в мс):
($time2 - $time1)*1000 / $calls


Аналогично для json
data size — размер сериализованных данных.

amount of enrites: 10000
data size: 387860
    calls: 182
serialize: 5.4978132247925 ms
data size: 268961
    calls: 90
     json: 11.220910814073 ms

amount of enrites: 50000
data size: 1983594
    calls: 73
serialize: 30.974479121049 ms
data size: 1344695
    calls: 39
     json: 57.372336890754 ms

amount of enrites: 240000
data size: 9703391
    calls: 18
serialize: 276.30117538543 ms
data size: 6454491
    calls: 18
     json: 279.90789217722 ms


Другое дело, что в контексте статьи это утверждение бессмысленно, ибо конфиги (мы же их рассматривали?) на 250 тысяч записей — это какой-то кошмар. И скрипту при таких размерах данных 64МБ уже не хватает.
И ведь не поспоришь :-)
Да, такие большие конфиги я не рассматривал
UFO just landed and posted this here
UFO just landed and posted this here
Насчет file():
Попробовал, на маленьких файлах получается чуточку быстрее, на больших — медленнее.
Спасибо, как освобожусь — включу в статью дополнение
UFO just landed and posted this here
Собственно, топик возник в результате размышлений над большими конфигами :-)
Конфиг (на мой взгляд) — это просто набор обязательных параметров для старта приложения. А вы оперируете этим словом как неким метасимволом «все, что настраиваемо».
Например, конфиги бывают разные у разных пользователей (темы оформления, как один пример из тысячи).

Я вижу понятие «конфиг» как некий «ключ для входа в пространство приложения». Такой конфиг никогда не разростется даже до масштаба 100 строк, поэтому его можно читать хоть побуквенно.

Далее — уже встает второй вопрос. Есть туча настроек конкретно этой сессии. Что делать с ними? ini/php-файлы на этом этапе выглядят уже странно. Я стараюсь вытащить на старте сессии все из базы, и закешировать (да, для анонимных сессий — в памяти, для авторизаций — в зависимости от посещаемости ресурса).
UFO just landed and posted this here
Эт вам на JavaEE надо пописать: полдня конфигуришь — полчаса пишешь =)

ЗЫ утрирую, конечно, но конфиги там по полной используются.
«Ключ для входа в пространство приложения» может занимать куда больше 100 строк даже без комментариев и пустых строк. Особенно в случае динамического связывания компонентов приложения, когда вам нужно чуть ли не для каждого поля класса (хорошо, если не объекта) указывать с какими другими классами и как он связан.
В парсинге текстового файла можно ещё чуть-чуть ускорить работу заменив:
foreach (explode("\n", file_get_contents($file)) as $line) {
на
foreach (file($file) as $line) {

Попробовал, на маленьких файлах получается чуточку быстрее, на больших — медленнее.
Спасибо, как освобожусь — включу в статью дополнение
UFO just landed and posted this here
С последним пунктом однозначно соглашусь — код должен генерировать по минимуму warning'ов и даже notice'ов. Но задача была оптимизировать работу скриптов по максимуму, избежать лишних проверок, и т.п. Чтобы не было «тормозов» из-за этих проверок. Поэтому код получился именно «тестовый», а не рабочий.
исследование ни о чём, кофиги нужно хранить в удобном виде, а затем кэшировать в шареную память: smop, apc, memcache…
подход ни о чём. куча слов о железе, но сейчас нет ничего о том, как делались замеры. тестировать лучше сторонними утилитами и парокой левых инклудов, чтобы исключить влияние файлового кэша ОС. например, ab с конкурентными запросами, как в реальной жизни.
> исследование ни о чём

Позвольте с Вами не согласиться

> кофиги нужно хранить в удобном виде, а затем кэшировать в шареную память: smop, apc, memcache…

Вообще-то да. Но сколько % людей, которые программируют на PHP, так делают?

> подход ни о чём. куча слов о железе

Ровно 1 предложение о железе было добавлено после того, как получено достаточно комментариев открыто и в личку

> но сейчас нет ничего о том, как делались замеры

Еще я не написал, что мой любимый редактор — Vim. Мне показалось это излишней информацией. А Вы из этого сделали вывод, что исследование и подход ни о чем

> тестировать лучше сторонними утилитами и парокой левых инклудов

Почему сторонними? Тогда ведь нужно считать время старта PHP-интерпретатора. Я тестировал из командной строки, время считалось вызовами microtime() до и после 1000 итераций загрузки конфигов

> чтобы исключить влияние файлового кэша ОС. например, ab с конкурентными запросами, как в реальной жизни.

А почему его нужно исключать?.. В реальной жизни же все используют кэш ОС. И если какой-то из алгоритмов использует его более эффективно — то ура, товарищи!
>Вообще-то да. Но сколько % людей, которые программируют на PHP, так делают?
А скольки процентам людей реально нужны эти миллисекунды чтения конфигов? ;-)
Когда я писал менеджер конфигов в средне-нагруженном проекте, то использовал SQL+memcache.
Да вряд ли кому.
Я вот чисто для интереса поисследовал :-)
>>Вообще-то да. Но сколько % людей, которые программируют на PHP, так делают?

те, кому нужно считать миллисекунды так делают.

>>Я тестировал из командной строки, время считалось вызовами microtime() до и после 1000 итераций загрузки конфигов

вот об этом я и писал, что ничего не было о том, как делаются замеры. а они делаются так как и предполагалось — цикл из ПОСЛЕДОВАТЕЛЬНЫХ запросов к ОДНОМУ файлу. эпик фэйл, не имеющий никакого отношений к реальной жизни.

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

Код:
$time = new DateTime();
echo "Start:".$time->getTimestamp().PHP_EOL;

$mc= new Memcache;
$mc->connect('localhost', 11211);

$key = "_config1000";

for ($i=0; $i<10000; $i++) {
  $config = $mc->get($key);

  if (!$config) {
    $config = parse_ini_file('test.ini');
    $mc->add($key, serialize($config));
  }
}


$time = new DateTime();
echo "End:".$time->getTimestamp().PHP_EOL;


ini-файл, 1000 параметров, 10000 итераций, без использования кеша:

Start:1295868628
End:1295868639

(11 секунд)

этот же файл с использованием memcache:
Start:1295868721
End:1295868723

(2 секунды)

Очевидно что лучше его таки использовать:)
$mc->add($key, serialize($config));

serialize() то зачем? драйвер мемкеша его автоматически делает. И Unserialize вы не делаете.

И время соединения с мемкешем только один раз посчитали, хотя нужно на каждой итерации делать connect()
Да, я не заморачивался, ну суть в принципе та же
Но вывод, скорее всего, будет другой. Особенно если время connect() посчитать для каждой итерации.

Хотя, если мемкеш используется в самом проекте и коннект нужно делать в любом случае, то да, можно время коннекта отбросить
Прогнал с исправлениями, получилось:
Start:1295874910
End:1295874923

Start:1295874893
End:1295874896

Redis тоже, кстати, показал 3 секунды:
$time = new DateTime();
echo "Start:".$time->getTimestamp().PHP_EOL;

require_once 'Rediska.php';

for ($i=0; $i<100000; $i++) {
  $options = array(
    'namespace' => 'Auto3_',
    'servers'   => array(
        array('host' => '127.0.0.1',
              'port' => '6379'
        )
    )
  );

  $rediska = new Rediska($options);

  $keyn = "__conf1000";
  $key = new Rediska_Key($keyn);
  $config = $key->getValue($keyn);

  if (!$config) {
    $config = parse_ini_file('test.ini');
    $key->setValue($config);
  }
}

$time = new DateTime();
echo "End:".$time->getTimestamp().PHP_EOL;

Только там 10 000 итераций, а не 100 000
Еще конфиги можно хранить в формате YML.

Довольно приятно читается.

Для парсинга мы применяем компоненту из фреймворка Symfony (http://components.symfony-project.org/yaml/).
Результат кешируем в виде php кода.

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

UFO just landed and posted this here
Я на пхп конечно давно уже не пишу. Но какой процент от общего времени выполнения занимает инициализация конфига?
UFO just landed and posted this here
А можно точнее? в процентах, мне действительно интересно что побудило автора провести такое исследование.
UFO just landed and posted this here
Предыстория простая.

Когда одним скриптом (ну, точнее, одним вызовом) генерируется большая HTML-страница — то % времени на чтение конфигов маленький. Ну, например, 1/1000 времени работы скрипта.

Когда скриптом генерируются картинки, то, возможно, чуть больше (зависит от многих условий, понятное дело). Условно, потому что картинки генерируются графическими библиотеками, там PHP-вызовов меньше.

Когда скриптом генерируются AJAX-ответы — то, бывает, еще больше.

Например, я недавно видел подобный скрипт, который позволял реализовать часы в браузере: время на часах через AJAX-запросы к серверу синхронизировалось с серверным. Так вот, чтобы отдать клиенту текущее время, скрипт предварительно лез в конфиги, чтобы посмотреть формат времени-даты. Это, конечно, можно считать мисдизайном, потому что формат можно было принимать от клиента. Но это как посмотреть.

Так вот, это я к чему… Чтение конфигов в этом скрипте составляло что-то около 90% времени. А запросов к нему было достаточно много (больше, чем к страницам). А теперь представьте, что этот скрипт случайно был бы включен в phpBB, и конфиги бы хранились в базе данных…

Отсюда и интерес к исследованию скорости: чем меньше скрипты и чем больше раз они вызываются, тем более оптимизированы они должны быть.
кхм, а почему бы время/дату не передавать в unix timestamp? конфиги вообще не нужны в таком случае :)
UFO just landed and posted this here
пока не могу себе представить такой проект, в котором были бы сотни мегабайт настроек…
после длительного использования symfony привык держать конфиги в yml файлах. С кэшированием в php-массивы, разумеется. Читать и править такие конфиги удобно, на выходе очень гибкий и быстрый php-код, без необходимости что-либо парсить при каждом запуске скрипта.
спасибо за полезную статью. Возьму на вооружение. Был удивлён высоким результатом ini-файлов.
Не пробовали хранить настройки в SQLIte? Интересно было бы добавить к тестам и такой вариант. Конечно он проиграет в сравнении с тем же INI или нативным PHP, но, иногда нужно дать возможность менять настройки через CMS, и вот тут выбор как лучше организовать хранение и куда писать эти настройки.
Sign up to leave a comment.

Articles