Комментарии 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 никогда об этом не задумывался. Об этом не нужно задумываться. По хорошему, я бы хотел увидеть пару функций
Если вернуться к паскалю — гляньте на оператор «^». Или вообще на спецификацию посмотрите. А то на «низком» уровне все программировать научились, а смысл термина как-то стерся.
Вы хотите поговорить, как работает PHP на низком уровне? — Тогда нужно сначала попрограммировать на C/C++, а потом заглядывать в код функций PHP (а не программировать на PHP непосредственно).
Хорошая абстракция языка всегда подразумевает, что хорошему программисту на этом языке не нужно знать тонкости реализации. Это правильно, что p00h никогда об этом не задумывался. Об этом не нужно задумываться. По хорошему, я бы хотел увидеть пару функций
store_config/load_config
, и не задаваться вопросом, какой из стапятисот предложенных авторами языка методов мне выбрать.Если вернуться к паскалю — гляньте на оператор «^». Или вообще на спецификацию посмотрите. А то на «низком» уровне все программировать научились, а смысл термина как-то стерся.
функции store_config/load_config — это уже не уровень языка, это более высокий уровень абстракции — фреймворки, etc, потому что config — это само по себе абстрактное понятие в отличие от xml, ini или что там еще.
А насчет паскаля — насколько я его помню, указатели в нем не настолько «продвинуты», как в Си. Поправьте меня, если я не прав.
А насчет паскаля — насколько я его помню, указатели в нем не настолько «продвинуты», как в Си. Поправьте меня, если я не прав.
Одна из первых заповедей оптимизации — используй native функции (если такая есть конечно). Они всегда быстрее. Альшанецкий говорил что в его практике только один раз кастомное решение на чистом PHP было быстрее нативной функции, а это о чем-то да говорит )
Стоит заметить, что внешние файлы с настройками должны быть в закрытой папке, либо на уровень выше www.
В случае конфигов на PHP это не обязательно. Даже если злоумышленник найдет config.php и откроет его через браузер, он увидит пустую страничку.
Кроме этого, доступ к конфигам можно ограничивать через настройки сервера (как вариант, .htaccess).
Но, в сущности, Вы правы — защищать конфиги — первое дело каждого программиста.
Кроме этого, доступ к конфигам можно ограничивать через настройки сервера (как вариант, .htaccess).
Но, в сущности, Вы правы — защищать конфиги — первое дело каждого программиста.
Когда начал читать, то тоже подумал, что INI будут хуже обычного PHP-файла с массивом. А оно вон как повернулось.
Прочитав статью меня чуть кондрашка не схватила…
Чтение сериализованного массива из файла и его последующая ансериализация быстрее чтения переменных из php-файла…
Сериализованные массивы в файлах эффективны, если ими можно заменить данные из БД.
Чтение сериализованного массива из файла и его последующая ансериализация быстрее чтения переменных из php-файла…
Сериализованные массивы в файлах эффективны, если ими можно заменить данные из БД.
Ваша кондрашка умеет читать? ;)
Парсить сериализованный массив менее ресурсоёмко, чем парсить php-код. Всё верно.
А на доступ к файлу смотреть не нужно, так как он имеет место в обоих случаях.
Но если php-код уже пропарсен php-акселератором, тогда его просто нужно выполнить.
Жаль не проверяли
Жаль не проверяли
Самое интересное, что всю жизнь считал, что php-код будет как раз-таки быстрее, и кешировал ini-файлы в php-код. Теперь-то буду знать, что эти усилия никому, оказывается, не нужны.
А сколько раз проводился каждый тест?
Эцсамое, а нельзя подгрузить весь «тяжёлый» хлам, а потом схоронить где-нибудь в memcache в сериализованном виде?
Тоже самое подумал. Хранить конфиги надо там где удобно (база, ini или xml файлы, главное не define'ами ;)), и при проблемах с производительностью просто кэшировать их. Хоть в мемкэш, хоть даже в файлы в сериализованном виде. Будет и быстро и удобно.
а чем так плохи defin'ы, можно поподробней?
Навскидку: глобальное пространство имен, невозможность переопределить.
насчет медленных согласен, внешний вид это субъективно, а вот глобальность — почему это плохо?
если речь идет о глобальном конфиге сайта
почему это хуже чем например делать
$cfg = config::getInstance();
в каждом месте где нужно узнать project_root?
я прошу прощения за возможное ламерство, просто хочу восполнить пробел в понимании, пользуясь моментом :)
если речь идет о глобальном конфиге сайта
почему это хуже чем например делать
$cfg = config::getInstance();
в каждом месте где нужно узнать project_root?
я прошу прощения за возможное ламерство, просто хочу восполнить пробел в понимании, пользуясь моментом :)
Глобальность это всегда плохо, а вместе с невозможностью переопределения ведет к сильному связыванию и жесткой архитектуре. Даже с синглтонами можно манипулировать конфигами (пример синтетический):
config->switchInstance('additional');
runMethod();
config->switchInstance('main');
function runMethod(){
$cfg = config::getInstance();
...
}
Вы в чем-то правы.
Но большинство реализаций с константами выглядят примерно так (схема главного скрипта):
include конфиг;
include классы x 10;
include коннект к базе данных;
константы при этом встречаются только в инклюдах в главном скрипте и в коннекте к базе данных. Вопрос: а смысл было их делать глобальными?
Хотя реализации, повторю, могут отличаться
Но большинство реализаций с константами выглядят примерно так (схема главного скрипта):
include конфиг;
include классы x 10;
include коннект к базе данных;
константы при этом встречаются только в инклюдах в главном скрипте и в коннекте к базе данных. Вопрос: а смысл было их делать глобальными?
Хотя реализации, повторю, могут отличаться
Невозможность определять массивы, объекты.
У меня только один вопрос — настройки подключения к memcache где хранить? :-)
Логичнее всего сохранять такую инфу в apc. 0.002 0.015 0.17
Какая библиотека использовалась для кеширования опкода?
Практический подход к оптимизации (на базе опытов).
Берем в руки микротайм и считаем в узловых точках скриптов время и считаем. В местах, где можно существенно сэкономить — экономим.
Я не могу сейчас уже привести примеры кода, но могу описать ситуацию, когда экономил на выводе больших массивов данных из базы. Удобно, например, при выводе детализаций по всякого рода аккаунтингу (трафик, звонки, смс и прочие тарифицируемые...). Такие данные характерны короткими справочниками с десятками записей и огромной таблицей CDR (собственно записей тарификации). Вот в таких ситуациях, чтение справочников целиком из базы в массив в памяти и использование этих массивов при выводе с одним простым запросом только к таблице CDR в базе, позволяется «разогнать» скрипт в сотни раз, по сравнению с решением в лоб — запросами с join-ами в базу данных.
Конечно, это скорее оптимизация СУБД, но средствами языка программирования. :)
Берем в руки микротайм и считаем в узловых точках скриптов время и считаем. В местах, где можно существенно сэкономить — экономим.
Я не могу сейчас уже привести примеры кода, но могу описать ситуацию, когда экономил на выводе больших массивов данных из базы. Удобно, например, при выводе детализаций по всякого рода аккаунтингу (трафик, звонки, смс и прочие тарифицируемые...). Такие данные характерны короткими справочниками с десятками записей и огромной таблицей CDR (собственно записей тарификации). Вот в таких ситуациях, чтение справочников целиком из базы в массив в памяти и использование этих массивов при выводе с одним простым запросом только к таблице CDR в базе, позволяется «разогнать» скрипт в сотни раз, по сравнению с решением в лоб — запросами с join-ами в базу данных.
Конечно, это скорее оптимизация СУБД, но средствами языка программирования. :)
А все-таки стоит проверить хранение конфигов в базе данных. Да, конфиги в базе данных сильно проигрывают в скорости на указанных вами условиях работы. Но стоит рассмотреть другие варианты: чтение 20 настроек при определенных 1000, сохранение настроек.
В ваших примерах используются разные функции для чтения файлов. Мне кажется это тоже повлияло на результат исследования.
Старался по минимуму использовать PHP-wrapper'ы для работы с файлами, только там, где это действительно необходимо. А на оптимизацию работы с файловой системой на уровне ядра PHP большинство людей повлиять не могут, поэтому она не оценивалась.
а я и не говорю что нужно влиять. Нужно было при замерах использовать одну и ту же функцию для чтения файлов, думаю результат будет отличным от Вашего.
Например, вместо include($file) использовать eval('?'.'>'.file_get_contents($file).'<'.'?')
Или Вы имели в виду, что нужно избавиться от fopen/fgets/fclose и заменить на foreach (explode('\n', file_get_contents($file)) as $line)?
С последним, пожалуй, соглашусь, разница, наверное, будет.
Или Вы имели в виду, что нужно избавиться от fopen/fgets/fclose и заменить на foreach (explode('\n', file_get_contents($file)) as $line)?
С последним, пожалуй, соглашусь, разница, наверное, будет.
Для полноты картины можно еще проверить JSON, YAML
Хотя YAML должен быть достаточно медленным из-за отсутствия встроенных решений для парсинга, интереснее все же JSON.
Очень удобно хранить настройки в YAML, который спокойно и быстро читает и пишет свежая версия Zend Framework.
Как бы есть нативное решение ru2.php.net/manual/en/book.yaml.php
Добавил JSON.
Методы парсинга данных средствами PHP, по идее, должны быть сравнимы с разбором текстовых файлов или даже медленнее.
Методы парсинга данных средствами PHP, по идее, должны быть сравнимы с разбором текстовых файлов или даже медленнее.
Провел тесты приведённых скриптов на своей машине и о чудо… Настройки хранимые в php-скриптах на 20% быстрее, чем файлы с сериализованными массивами. Совершенно противоположные результаты.
Ошибка автора:
— Не учтено кэширование файлов ФС и кэширование данных PHP. Так как оба метода хранения данных (php-скрипты и сериализованные массивы в файлах) относительно мало различаются по скорости, то «играясь» с настройками кэширования можно добиться совершенно разных результатов.
На практике данный пример может действовать как и в положительную сторону, так и в отрицательную.
Ошибка автора:
— Не учтено кэширование файлов ФС и кэширование данных PHP. Так как оба метода хранения данных (php-скрипты и сериализованные массивы в файлах) относительно мало различаются по скорости, то «играясь» с настройками кэширования можно добиться совершенно разных результатов.
На практике данный пример может действовать как и в положительную сторону, так и в отрицательную.
XML надо было парсить через SimpleXML, результат был бы лучше. (проверено на собственной шкуре)
И еще, если на сервер поставить xcache — то чтение php файла с конфигом будет происходить мгновенно из рама (без учета дисковой операции). И по теории будет быстрее.
И еще, если на сервер поставить 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
В случае файла настроек, который будет преобразован в хеш-массив а DOM как таковой нафиг не нужен, нужно парсить потоковым SAX парсером.
Если стоит задача только читать конфиг из програмы (менять только ручками в редакторе), то быстрее будет делать через XMLReader. Особенно заметно на больших файлах.
Правда конфиг прийдется привести к другому виду:
1
2
3
Правда конфиг прийдется привести к другому виду:
1
2
3
Вообще-то если начинаешь задумываться о скорости, имеет смысл держать все в памяти – и настройки, и общие данные. Но в этом случае PHP, естественно, исключен.
Все эти танцы с бубном опять лишь для красоты, результаты предопределены. Вопрос в том, что делать дальше? Если это большой проект, где конфигурирование происходит из различных точек, никто не будет это переменными, ini файлами (многоуровневы конфигурации). Остается XML и JSON. Чаще всего применяется первый, не смотря на свою избыточность. Но никто не будет оставлять сборку и считыванию конфигов в голов виде. Будут кешировать на диск, в память, в memcache в результате ваша проделанная работа так и не раскрывает вопрос. Это сродни — функции работают быстрее классов, но мы ведь не будем все строить без ООП?
у нас ini + key-value кеш (redis, memcache)
Тестирование не совсем верное: нет гарантий что все использованные алгоритмы имеют линейную скорость работы и вдруг не завтормозят на десятке значений.
Правильно бы было сгенерировать конфиг файл длиной хотя бы несколько десятков строк: это будет ближе к реальной ситуации.
Правильно бы было сгенерировать конфиг файл длиной хотя бы несколько десятков строк: это будет ближе к реальной ситуации.
Вот оно как всё… Никогда раньше особо не задумывался над конфигами.
Простите мою глупость, но терзает вопрос, – Что за магические три числа в ваших результатах? Я не придираюсь, а правда не понял…
Это называется экономия на спичках. (Говоря русским языком, всё это онанизм для мозгов, и не более.)
Как я обычно говорю «если программисту кажется что тормозит язык программирования, значит тормозит сам программист.»
Как я обычно говорю «если программисту кажется что тормозит язык программирования, значит тормозит сам программист.»
Спасибо!
Расскажите, пожалуйста, как Вы делаете диаграммы?
Я 90% времени в консоли, и, к стыду своему, не знаю, как это делается :-)
Расскажите, пожалуйста, как Вы делаете диаграммы?
Я 90% времени в консоли, и, к стыду своему, не знаю, как это делается :-)
В консоли тоже делается, gnuplot'ом :)
Жесть. Не?
Вот как бы реально действующий код…
$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 = 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мбит =)
Данные примеры будут полезны, только при очень большом наплыве пользователей
Это не показатель крутости у меня на атоме, 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 строки
И там ниже ↓ – што-што, простите? Не понял – вы для генерации одной страницы «считываете конфиг» более одного раза?
<?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 строки
И там ниже ↓ – што-што, простите? Не понял – вы для генерации одной страницы «считываете конфиг» более одного раза?
> Ну почему у него в лучшем случае конфиг читается всего в два раза быстрее, чем у меня весь сайт работает (с известными шаблонизатором и БД)?!
Время считается для цикла из 1000 загрузок конфига. А у Вас генерируется 1 страница
Время считается для цикла из 1000 загрузок конфига. А у Вас генерируется 1 страница
Может быть автор диаграммы в топик перенесет?
Смею не согласится с автором.
Настройки читаются один раз и доступны на протяжении работы сценария, т. ч. считаю некорректным сравнивать способы их хранения и чтения. Вполне очевидно, что эти миллисекунды (один раз прочитать файл конфига), не так уж и важны, по сравнению с оптимизацией запросов и структурой БД.
Никто никогда не сомневался, парсинг XML в DOM это дорогая операция, но за удобство всегда нужно платить.
Откровенно говоря, все зависит от общей схемы работы и структуры.
Для меня удобнее хранить в XML (в атрибутах узлов я могу хранить что угодно и парсить данные на основе этого).
Настройки читаются один раз и доступны на протяжении работы сценария, т. ч. считаю некорректным сравнивать способы их хранения и чтения. Вполне очевидно, что эти миллисекунды (один раз прочитать файл конфига), не так уж и важны, по сравнению с оптимизацией запросов и структурой БД.
Никто никогда не сомневался, парсинг XML в DOM это дорогая операция, но за удобство всегда нужно платить.
Откровенно говоря, все зависит от общей схемы работы и структуры.
Для меня удобнее хранить в XML (в атрибутах узлов я могу хранить что угодно и парсить данные на основе этого).
> астройки читаются один раз и доступны на протяжении работы сценария
А это как написать :)
А это как написать :)
Где можно сделать запрос на Хабраэффект? :)
[a href=«yousite.com/»]сиськи здесь [/a] (В BB тегах потому что так надо :)
Я сказал, что для оптимизации (обычно) есть более подходящие процессы.
интересно было бы узнать какими либами и их версиями проводилось считывание, а также резличие в одних и тех же форматах для различных поставленных вами «условий»… а то картинка не чёткая…
модераторы удалите топик, не позорьте ресурс
А я вообще ничего не понял, у меня и так все нормально работает.
чтобы JSON возвращал не stdClass object, а массив надо в функции json_decode 2 параметром дописать true
Опять экономят крохи не обращая внимания на слонов.
ini файлы — наиболее удобны в использовании, ими и надо пользоваться.
ini файлы — наиболее удобны в использовании, ими и надо пользоваться.
Кстати при увеличении массива данных, json_decode себя покажет куда лучше чем unserialize.
У Вас есть весомые аргументы для этого?
Массив заполняется $size (amount of enrites в результатах) случайными строковыми значениями:
8-16 символов ord(rand(32, 128));
Затем сериализуется.
Время выполнения предлагается:
Дальше идёт адаптивная схема измерения времени выполнения
время выполнения одного вызова (в мс):
Аналогично для json
data size — размер сериализованных данных.
Другое дело, что в контексте статьи это утверждение бессмысленно, ибо конфиги (мы же их рассматривали?) на 250 тысяч записей — это какой-то кошмар. И скрипту при таких размерах данных 64МБ уже не хватает.
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МБ уже не хватает.
Насчет file():
Попробовал, на маленьких файлах получается чуточку быстрее, на больших — медленнее.
Спасибо, как освобожусь — включу в статью дополнение
Попробовал, на маленьких файлах получается чуточку быстрее, на больших — медленнее.
Спасибо, как освобожусь — включу в статью дополнение
Собственно, топик возник в результате размышлений над большими конфигами :-)
Конфиг (на мой взгляд) — это просто набор обязательных параметров для старта приложения. А вы оперируете этим словом как неким метасимволом «все, что настраиваемо».
Например, конфиги бывают разные у разных пользователей (темы оформления, как один пример из тысячи).
Я вижу понятие «конфиг» как некий «ключ для входа в пространство приложения». Такой конфиг никогда не разростется даже до масштаба 100 строк, поэтому его можно читать хоть побуквенно.
Далее — уже встает второй вопрос. Есть туча настроек конкретно этой сессии. Что делать с ними? ini/php-файлы на этом этапе выглядят уже странно. Я стараюсь вытащить на старте сессии все из базы, и закешировать (да, для анонимных сессий — в памяти, для авторизаций — в зависимости от посещаемости ресурса).
Например, конфиги бывают разные у разных пользователей (темы оформления, как один пример из тысячи).
Я вижу понятие «конфиг» как некий «ключ для входа в пространство приложения». Такой конфиг никогда не разростется даже до масштаба 100 строк, поэтому его можно читать хоть побуквенно.
Далее — уже встает второй вопрос. Есть туча настроек конкретно этой сессии. Что делать с ними? ini/php-файлы на этом этапе выглядят уже странно. Я стараюсь вытащить на старте сессии все из базы, и закешировать (да, для анонимных сессий — в памяти, для авторизаций — в зависимости от посещаемости ресурса).
Эт вам на JavaEE надо пописать: полдня конфигуришь — полчаса пишешь =)
ЗЫ утрирую, конечно, но конфиги там по полной используются.
ЗЫ утрирую, конечно, но конфиги там по полной используются.
«Ключ для входа в пространство приложения» может занимать куда больше 100 строк даже без комментариев и пустых строк. Особенно в случае динамического связывания компонентов приложения, когда вам нужно чуть ли не для каждого поля класса (хорошо, если не объекта) указывать с какими другими классами и как он связан.
В парсинге текстового файла можно ещё чуть-чуть ускорить работу заменив:
foreach (explode("\n", file_get_contents($file)) as $line) {
на
foreach (file($file) as $line) {
foreach (explode("\n", file_get_contents($file)) as $line) {
на
foreach (file($file) as $line) {
Попробовал, на маленьких файлах получается чуточку быстрее, на больших — медленнее.
Спасибо, как освобожусь — включу в статью дополнение
Спасибо, как освобожусь — включу в статью дополнение
С последним пунктом однозначно соглашусь — код должен генерировать по минимуму warning'ов и даже notice'ов. Но задача была оптимизировать работу скриптов по максимуму, избежать лишних проверок, и т.п. Чтобы не было «тормозов» из-за этих проверок. Поэтому код получился именно «тестовый», а не рабочий.
исследование ни о чём, кофиги нужно хранить в удобном виде, а затем кэшировать в шареную память: smop, apc, memcache…
подход ни о чём. куча слов о железе, но сейчас нет ничего о том, как делались замеры. тестировать лучше сторонними утилитами и парокой левых инклудов, чтобы исключить влияние файлового кэша ОС. например, ab с конкурентными запросами, как в реальной жизни.
подход ни о чём. куча слов о железе, но сейчас нет ничего о том, как делались замеры. тестировать лучше сторонними утилитами и парокой левых инклудов, чтобы исключить влияние файлового кэша ОС. например, ab с конкурентными запросами, как в реальной жизни.
> исследование ни о чём
Позвольте с Вами не согласиться
> кофиги нужно хранить в удобном виде, а затем кэшировать в шареную память: smop, apc, memcache…
Вообще-то да. Но сколько % людей, которые программируют на PHP, так делают?
> подход ни о чём. куча слов о железе
Ровно 1 предложение о железе было добавлено после того, как получено достаточно комментариев открыто и в личку
> но сейчас нет ничего о том, как делались замеры
Еще я не написал, что мой любимый редактор — Vim. Мне показалось это излишней информацией. А Вы из этого сделали вывод, что исследование и подход ни о чем
> тестировать лучше сторонними утилитами и парокой левых инклудов
Почему сторонними? Тогда ведь нужно считать время старта PHP-интерпретатора. Я тестировал из командной строки, время считалось вызовами microtime() до и после 1000 итераций загрузки конфигов
> чтобы исключить влияние файлового кэша ОС. например, ab с конкурентными запросами, как в реальной жизни.
А почему его нужно исключать?.. В реальной жизни же все используют кэш ОС. И если какой-то из алгоритмов использует его более эффективно — то ура, товарищи!
Позвольте с Вами не согласиться
> кофиги нужно хранить в удобном виде, а затем кэшировать в шареную память: smop, apc, memcache…
Вообще-то да. Но сколько % людей, которые программируют на PHP, так делают?
> подход ни о чём. куча слов о железе
Ровно 1 предложение о железе было добавлено после того, как получено достаточно комментариев открыто и в личку
> но сейчас нет ничего о том, как делались замеры
Еще я не написал, что мой любимый редактор — Vim. Мне показалось это излишней информацией. А Вы из этого сделали вывод, что исследование и подход ни о чем
> тестировать лучше сторонними утилитами и парокой левых инклудов
Почему сторонними? Тогда ведь нужно считать время старта PHP-интерпретатора. Я тестировал из командной строки, время считалось вызовами microtime() до и после 1000 итераций загрузки конфигов
> чтобы исключить влияние файлового кэша ОС. например, ab с конкурентными запросами, как в реальной жизни.
А почему его нужно исключать?.. В реальной жизни же все используют кэш ОС. И если какой-то из алгоритмов использует его более эффективно — то ура, товарищи!
>Вообще-то да. Но сколько % людей, которые программируют на PHP, так делают?
А скольки процентам людей реально нужны эти миллисекунды чтения конфигов? ;-)
Когда я писал менеджер конфигов в средне-нагруженном проекте, то использовал SQL+memcache.
А скольки процентам людей реально нужны эти миллисекунды чтения конфигов? ;-)
Когда я писал менеджер конфигов в средне-нагруженном проекте, то использовал SQL+memcache.
>>Вообще-то да. Но сколько % людей, которые программируют на PHP, так делают?
те, кому нужно считать миллисекунды так делают.
>>Я тестировал из командной строки, время считалось вызовами microtime() до и после 1000 итераций загрузки конфигов
вот об этом я и писал, что ничего не было о том, как делаются замеры. а они делаются так как и предполагалось — цикл из ПОСЛЕДОВАТЕЛЬНЫХ запросов к ОДНОМУ файлу. эпик фэйл, не имеющий никакого отношений к реальной жизни.
я бы прошёл мимо, если бы не пришлось последних два месяца разгребать проект, сделаный такими горе-оптимизаторами. вы здесь понаписовали и многие принимают это за чистую монету и будут завтра с пеной у рта доказывать, они же на хабре прочитали. тьфу.
те, кому нужно считать миллисекунды так делают.
>>Я тестировал из командной строки, время считалось вызовами microtime() до и после 1000 итераций загрузки конфигов
вот об этом я и писал, что ничего не было о том, как делаются замеры. а они делаются так как и предполагалось — цикл из ПОСЛЕДОВАТЕЛЬНЫХ запросов к ОДНОМУ файлу. эпик фэйл, не имеющий никакого отношений к реальной жизни.
я бы прошёл мимо, если бы не пришлось последних два месяца разгребать проект, сделаный такими горе-оптимизаторами. вы здесь понаписовали и многие принимают это за чистую монету и будут завтра с пеной у рта доказывать, они же на хабре прочитали. тьфу.
графиков нехватает… было бы намного удобнее подрезюмировать
Добрые комментаторы уже сделали =)
habrahabr.ru/blogs/php/112402/#comment_3597165
habrahabr.ru/blogs/php/112402/#comment_3597165
Вообще, ИМХО исследование бесполезно по определению. Все равно, где хранить данный. Нужно выбирать тот формат, который удобней. Если вы рулите реально большой системой, на которой будет заметны такие мелочи, то вы будете кешировать конфиг.
Просто кешируем прочитанные с конфига значения и нам все равно сколько они парястя первый раз, главное что бы было удобно менять их.
Выложите архив со скриптами для тестирования, чтобы каждый мог запустить на своей системе и проверить, что будет быстрей.
Пожалуйста.
Извините за написанный на скорую руку код :-)
Извините за написанный на скорую руку код :-)
Провел небольшой тест:
Код:
ini-файл, 1000 параметров, 10000 итераций, без использования кеша:
Start:1295868628
End:1295868639
(11 секунд)
этот же файл с использованием memcache:
Start:1295868721
End:1295868723
(2 секунды)
Очевидно что лучше его таки использовать:)
Код:
$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()
Да, я не заморачивался, ну суть в принципе та же
Прогнал с исправлениями, получилось:
Start:1295874910
End:1295874923
Start:1295874893
End:1295874896
Redis тоже, кстати, показал 3 секунды:
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;
Еще конфиги можно хранить в формате YML.
Довольно приятно читается.
Для парсинга мы применяем компоненту из фреймворка Symfony (http://components.symfony-project.org/yaml/).
Результат кешируем в виде php кода.
После сохранения результата в кеш быстродействие будет сравнимо с хранением конфигов в виде обычного PHP.
Довольно приятно читается.
Для парсинга мы применяем компоненту из фреймворка Symfony (http://components.symfony-project.org/yaml/).
Результат кешируем в виде php кода.
После сохранения результата в кеш быстродействие будет сравнимо с хранением конфигов в виде обычного PHP.
Я на пхп конечно давно уже не пишу. Но какой процент от общего времени выполнения занимает инициализация конфига?
А можно точнее? в процентах, мне действительно интересно что побудило автора провести такое исследование.
Предыстория простая.
Когда одним скриптом (ну, точнее, одним вызовом) генерируется большая HTML-страница — то % времени на чтение конфигов маленький. Ну, например, 1/1000 времени работы скрипта.
Когда скриптом генерируются картинки, то, возможно, чуть больше (зависит от многих условий, понятное дело). Условно, потому что картинки генерируются графическими библиотеками, там PHP-вызовов меньше.
Когда скриптом генерируются AJAX-ответы — то, бывает, еще больше.
Например, я недавно видел подобный скрипт, который позволял реализовать часы в браузере: время на часах через AJAX-запросы к серверу синхронизировалось с серверным. Так вот, чтобы отдать клиенту текущее время, скрипт предварительно лез в конфиги, чтобы посмотреть формат времени-даты. Это, конечно, можно считать мисдизайном, потому что формат можно было принимать от клиента. Но это как посмотреть.
Так вот, это я к чему… Чтение конфигов в этом скрипте составляло что-то около 90% времени. А запросов к нему было достаточно много (больше, чем к страницам). А теперь представьте, что этот скрипт случайно был бы включен в phpBB, и конфиги бы хранились в базе данных…
Отсюда и интерес к исследованию скорости: чем меньше скрипты и чем больше раз они вызываются, тем более оптимизированы они должны быть.
Когда одним скриптом (ну, точнее, одним вызовом) генерируется большая HTML-страница — то % времени на чтение конфигов маленький. Ну, например, 1/1000 времени работы скрипта.
Когда скриптом генерируются картинки, то, возможно, чуть больше (зависит от многих условий, понятное дело). Условно, потому что картинки генерируются графическими библиотеками, там PHP-вызовов меньше.
Когда скриптом генерируются AJAX-ответы — то, бывает, еще больше.
Например, я недавно видел подобный скрипт, который позволял реализовать часы в браузере: время на часах через AJAX-запросы к серверу синхронизировалось с серверным. Так вот, чтобы отдать клиенту текущее время, скрипт предварительно лез в конфиги, чтобы посмотреть формат времени-даты. Это, конечно, можно считать мисдизайном, потому что формат можно было принимать от клиента. Но это как посмотреть.
Так вот, это я к чему… Чтение конфигов в этом скрипте составляло что-то около 90% времени. А запросов к нему было достаточно много (больше, чем к страницам). А теперь представьте, что этот скрипт случайно был бы включен в phpBB, и конфиги бы хранились в базе данных…
Отсюда и интерес к исследованию скорости: чем меньше скрипты и чем больше раз они вызываются, тем более оптимизированы они должны быть.
пока не могу себе представить такой проект, в котором были бы сотни мегабайт настроек…
после длительного использования symfony привык держать конфиги в yml файлах. С кэшированием в php-массивы, разумеется. Читать и править такие конфиги удобно, на выходе очень гибкий и быстрый php-код, без необходимости что-либо парсить при каждом запуске скрипта.
спасибо за полезную статью. Возьму на вооружение. Был удивлён высоким результатом ini-файлов.
Не пробовали хранить настройки в SQLIte? Интересно было бы добавить к тестам и такой вариант. Конечно он проиграет в сравнении с тем же INI или нативным PHP, но, иногда нужно дать возможность менять настройки через CMS, и вот тут выбор как лучше организовать хранение и куда писать эти настройки.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Самые быстрые настройки для PHP-скриптов