Самое узкое место в ЛС - увеличение производительности

Ища пути ускорения движка я постоянно спотыкаюсь об класс конфига (/engine/lib/internal/ConfigSimple/Config.class.php), а именно — меня беспокоит авто замена ключей в конфиге. Такой код мы можем найти в дистрибутиве:

static public function KeyReplace($cfg,$sInstance=self::DEFAULT_CONFIG_INSTANCE) {
  if(is_array($cfg)) {
    foreach($cfg as $k=>$v) {
      $k_replaced = self::KeyReplace($k, $sInstance);
      if($k==$k_replaced) {
        $cfg[$k] = self::KeyReplace($v,$sInstance);
      } else {
        $cfg[$k_replaced] = self::KeyReplace($v,$sInstance);
        unset($cfg[$k]);
      }
    }
  } else {
    if(preg_match('~___([\S|\.|]+)___~Ui',$cfg))
      $cfg = preg_replace_callback(
        '~___([\S|\.]+)___~Ui',
        create_function('$value','return Config::Get($value[1],"'.$sInstance.'");'),
        $cfg
      );
  }
  return $cfg;
}


и тут уже стает ясно одно — машина регулярных выражений запускается ПОСТОЯННО ДЛЯ КАЖДОГО ПОЛУЧЕНИЯ КЛЮЧА, независимо от того нужно это или нет + мелкая ошибка в регулярке. Поразмыслив, можно составить такой плагин быстрой переделки:

  1. не нужно запускать регулярку, если нечего заменять, а проверку сделать быстрыми средствами языка, вместо регулярных выражений
  2. избавить существующую регулярку от лишнего «ИЛИ» и избавить от лишнего модификатора

несложные манипуляции с кодом и получаем результат:

static public function KeyReplace($cfg,$sInstance=self::DEFAULT_CONFIG_INSTANCE) {
  if(is_array($cfg)) {
    foreach($cfg as $k=>$v) {
      $k_replaced = self::KeyReplace($k, $sInstance);
      if($k==$k_replaced) {
        $cfg[$k] = self::KeyReplace($v,$sInstance);
      } else {
        $cfg[$k_replaced] = self::KeyReplace($v,$sInstance);
        unset($cfg[$k]);
      }
    }
  } else {
    if (strpos ($cfg, '___') !== false) {
      $cfg = preg_replace_callback (
        '~___([\S|\.]+)___~U',
        create_function ('$value', 'return Config::Get($value[1],"'.$sInstance.'");'),
        $cfg
      );
    }
  }
  return $cfg;
}

Скорость выполнения от такой модификации не меняется, а потребление памяти несколько уменьшается.

З.Ы. это кросспост отсюда.

36 комментариев

avatar
Спасибо.
avatar
Да и лямбда-функцию тут имеет смысл заменить на обычную
avatar
тоже думал, но не нашел нигде подтверждения того, что когда-то читал что они не всегда высвобождают память.

кстати там в конфиге есть приведение типов в цикле, например:

	public function GetValue($sKey, $sInstance=self::DEFAULT_CONFIG_INSTANCE) {
		// Return config by path (separator=".")
		$aKeys=explode('.',$sKey);

		$cfg=$this->GetConfig();
		foreach ((array)$aKeys as $sK) {
			if(isset($cfg[$sK])) {
				$cfg=$cfg[$sK];
			} else {
				return null;
			}
		}

		$cfg = self::KeyReplace($cfg,$sInstance);
		return $cfg;
	}

думаю что можно было бы вынести:

	public function GetValue($sKey, $sInstance=self::DEFAULT_CONFIG_INSTANCE) {
		// Return config by path (separator=".")
		$aKeys=(array) explode('.',$sKey);

		$cfg=$this->GetConfig();
		foreach ($aKeys as $sK) {
			if(isset($cfg[$sK])) {
				$cfg=$cfg[$sK];
			} else {
				return null;
			}
		}

		$cfg = self::KeyReplace($cfg,$sInstance);
		return $cfg;
	}
avatar
тоже думал, но не нашел нигде подтверждения того, что когда-то читал что они не всегда высвобождают память.
Даже если память гарантировано освобождается, то чисто логически: если при загрузке страницы будет сто обращений к этому коду, то это значит, что СТО раз будет создаваться и компилироваться новая функция, а потом она будет уничтожаться. Тут явно лучше ОДИН раз объявить функцию, которая будет создана при компиляции, а потом сто раз вызывать уже скомпилиованную ф-ю.
avatar
там ещё передается второй параметр — sInstance, вот в чем сложность
avatar
Чота мне кажется, что можно вообще без коллбек тут обойтись: через preg_match_all получить все ссылки на ключи, а потом в цикле их заменить на значения. Понимаю, что чисто внешне код становится менее изящным, но есть внутреннее ощущение, что он будет более производительным
avatar
livestreet.ru/blog/addons/13068.html — вот тут писал, что память не освобождается. Плюс еще писал, как победить это :) Точнее какой костыль поставить. Видимо класс конфига нужно очень сильно переработать.
avatar
Видимо класс конфига нужно очень сильно переработать.
согласен. и нетолько класс конфига, ещё есть места.
нужно время только для теста всего этого…
avatar
тоже думал, но не нашел нигде подтверждения того, что когда-то читал что они не всегда высвобождают память.
Точнее сказать, они никогда не освобождают память.

Из за этого в ОРМ был баг с сжиранием памяти
avatar
а заче там вообще preg_match или strpos? они же на**й не нужны
avatar
чтобы не
машина регулярных выражений запускается ПОСТОЯННО ДЛЯ КАЖДОГО ПОЛУЧЕНИЯ КЛЮЧА
т.е. экономить память
avatar
вы тут на спичках экономите. замеры делались сравнительные?
avatar
вы тут на спичках экономите.
когда конфиг вызывается несколько тыщ раз, то тогда эти сотые доли секунды начинают показываться.
замеры делались сравнительные
да, прироста по скорости почти нет, а память жрет меньше
avatar
замеры делались сравнительные
да, прироста по скорости почти нет, а память жрет меньше
ну тогда с богом
avatar
Чтоб парсить ключи типа ___key1___.key2
avatar
он наверное хотел сказать чтобы без проверок сразу вызывать регулярку: найдет — заменит, а нет — пропустит.
avatar
yes.
avatar
ну тогда мой ответ выше подходит
avatar
Как кстати :) Перевожу проект с 0.4.2 на 1.0.1… Запустил у себя пересчет дерева комментов и вот здраствуйте:

Fatal error: Allowed memory size of 67108864 bytes exhausted (tried to allocate 7680 bytes) in /home/kerby/v20.writercenter.ru/www/engine/lib/internal/ConfigSimple/Config.class.php(197): runtime-created function on line 1

Зло, зло! :)
avatar
уже все просили сделать минимальной версией пхп для лс 5.3.
если Максим согласится — можно перелопатить большую кучу кода.
avatar
Это да. Но не первостепенно уж. Какие-то баги и реальные улучшения вносить — да, но без фанатизма. Руководствуясь принципом «не трогай отлаженный код» :)
avatar
ну не весь код, но вот изьять create_function из кода совсем — было бы гуд. в ЛС 1.0.1 сейчас их встречается около 10, причем некоторые в циклах, как например при формировании ЖС и КСС файлов, получение ключей конфига и т.п.

это могло бы уменьшить расход памяти.
avatar
Ну, по хорошему, от них в любом случае надо избавляться — даже и без перехода.
avatar
надо, но тогда планку пхп нужно ставить выше
avatar
Хоть я нифига не рублю в программировании, мне все равно приятно читать такого рода топики.
комментарий был удален
avatar
  • ort
  • 0
avatar
а если будет несколько ключей типа:

___пас.рут.веб___/___мой.супер.ключ___

?
в таком случае будет обработан только первый ключ
avatar
avatar
префекционизм принимается?)
ключ i в регекспе не нужен.

На чистой ЛС 1.0.1 в условиях теста память стало жрать на пол мегабайте меньше.
avatar
* перфекционизм
avatar
ну и утечкам можно сказать «пока».

Правда поиском в движку можно с легкостью найти ещё вызовы лямбда функций в циклах. Вот бы и их заменить…
avatar
А когда планируется LS 1.0.2? 5 месяцев с 1.0.1 прошло, в github изрядно фиксов всяких полезных с тех пор. Было бы круто.
avatar
у меня ещё есть пара идей оптимизации.
хотелось бы их все 102 вместить так чтобы общий прирост производительности заставил рот открыться.
ну и исправления безопасности.

будет самая быстрая и стабильная версия)
avatar
Да, было бы хорошо, получить такой подарок под елочку :)
avatar
Это всё замечательно, но долго ли ждать? :)
avatar
не знаю. Максиму лучше видно.
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.