Оптимизация кода

Статус
В этой теме нельзя размещать новые ответы.

CrashX

В прошлом XSiteCMS
Регистрация
6 Июн 2008
Сообщения
682
Реакции
114
есть загвоздка при включении Safe_mode=on
скрипт притормаживает выполнение на компе
#1 было 0,31 стало 2,52
#2 было 0,12 стало 0,83
в отладчике нашел наиболее долго выполняемые функции
~ 30-40% задержки
это ob_get_contents(); но замена на file_get_contents($tpl);
нерешает проблемы...с
~ 20-26% задержки
file_exists() нужно делать

расход памяти не увеличивается. только время выполнения,




код меню в том виде в котором попутает в функцию
Код:
 $menu = array(
        array('name' => 'Новости', 'link' => WWW . 'news.html', 'image' => '01.png', 'block' =>
            array(
                array('name' => 'Добавить', 'link' => WWW . 'news/add.html', 'image' => '03.png', 'block' => ''),
                array('name' => 'Категории', 'link' => WWW . '#', 'image' => '04.png', 'block' => ''),
                array('name' => 'Комментарии', 'link' => WWW . '#', 'image' => '05.png', 'block' => ''),
            )
        ),
        array('name' => 'Новый 2', 'link' => WWW . '#', 'image' => '02.png', 'block' =>
            array(
                array('name' => 'Новый 3', 'link' => WWW . '#', 'image' => '03.png', 'block' => ''),
                array('name' => 'Новый 4', 'link' => WWW . '#', 'image' => '04.png', 'block' => ''),
                array('name' => 'Новый 51', 'link' => WWW . '#', 'image' => '05.png', 'block' =>
                    array(
                        array('name' => 'Новый 3', 'link' => WWW . '#', 'image' => '03.png', 'block' => ''),
                        array('name' => 'Новый 4', 'link' => WWW . '#', 'image' => '04.png', 'block' => ''),
                        array('name' => 'Новый 52', 'link' => WWW . '#', 'image' => '05.png', 'block' =>
                            array(
                                array('name' => 'Новый 3', 'link' => WWW . '#', 'image' => '03.png', 'block' => ''),
                                array('name' => 'Новый 4', 'link' => WWW . '#', 'image' => '04.png', 'block' => ''),
                                array('name' => 'Новый 53', 'link' => WWW . '#', 'image' => '05.png', 'block' =>
                                    array(
                                        array('name' => 'Новый 3', 'link' => WWW . '#', 'image' => '03.png', 'block' => ''),
                                        array('name' => 'Новый 4', 'link' => WWW . '#', 'image' => '04.png', 'block' => ''),
                                        array('name' => 'Новый 54', 'link' => WWW . '#', 'image' => '05.png', 'block' => ''),
                                    )
                                )
                            )
                        )
                    )
                )
            ),
        ),
        array('name' => 'Новый 3', 'link' => WWW . '#', 'image' => '03.png', 'block' => ''),
        array('name' => 'Новый 4', 'link' => WWW . '#', 'image' => '04.png', 'block' => ''),
        array('name' => 'Новый 5', 'link' => WWW . '#', 'image' => '05.png', 'block' =>
            array(
                array('name' => 'Новый 3', 'link' => WWW . '#', 'image' => '03.png', 'block' => ''),
                array('name' => 'Новый 4', 'link' => WWW . '#', 'image' => '04.png', 'block' => ''),
                array('name' => 'Новый 51', 'link' => WWW . '#', 'image' => '05.png', 'block' =>
                    array(
                        array('name' => 'Новый 3', 'link' => WWW . '#', 'image' => '03.png', 'block' => ''),
                        array('name' => 'Новый 4', 'link' => WWW . '#', 'image' => '04.png', 'block' => ''),
                        array('name' => 'Новый 52', 'link' => WWW . '#', 'image' => '05.png', 'block' =>
                            array(
                                array('name' => 'Новый 3', 'link' => WWW . '#', 'image' => '03.png', 'block' => ''),
                                array('name' => 'Новый 4', 'link' => WWW . '#', 'image' => '04.png', 'block' => ''),
                                array('name' => 'Новый 53', 'link' => WWW . '#', 'image' => '05.png', 'block' =>
                                    array(
                                        array('name' => 'Новый 3', 'link' => WWW . '#', 'image' => '03.png', 'block' => ''),
                                        array('name' => 'Новый 4', 'link' => WWW . '#', 'image' => '04.png', 'block' => ''),
                                        array('name' => 'Новый 54', 'link' => WWW . '#', 'image' => '05.png', 'block' => ''),
                                    )
                                )
                            )
                        )
                    )
                )
            )
        )
    );
шаблоны
Код:
menu.block.tpl.php
<div class='menupanel'>
{menu}
</div>

menu.items.tpl.php
<li {class}><a href='{link}'><img src='{image}' alt='{name}' align='middle' border='0' />{name}</a>{block}</li>


menu.union.tpl.php
<ul {class}>
  {union}
</ul>

шаблонизатор
Код:
<?
class Template {

  var $version = 0.01;
  var $template = null;
  var $module = null;
  var $panel = null;
  var $block = null;
  var $backup = array();
  var $data = array();
  var $result = array();
  var $standard = array();

  function generator() {
    global $engine;
    if (!defined('__DATA')):
      $this->standard = array(
          '{_doctype_}' =>    $engine->doctype(DOCTYPE),
          '{_meta_}' =>       $engine->meta(),
          '{_doctype_}' =>    $engine->doctype(DOCTYPE),
          '{_title_}' =>      $engine->config['title'],
          '{_style_}' =>      STYLE_DIR . $engine->config['template'],
          '{_welcome_}' =>    $engine->module->load('bar', 'panel', 'welcome', null, null, null, false),
          '{_menu_}' =>       $engine->module->load('bar', 'panel', 'menu', null, null, null, false),
          '{_userbar_}' =>    $engine->module->load('bar', 'panel', 'userbar', null, null, null, false),
          '{_pathway_}' =>    $engine->module->load('bar', 'panel', 'pathway', null, null, null, false),
          '{_copyright_}' =>  COPYRIGHT . "&nbsp;" . sprintf(_WORKS, PRODUCT),
          '{_product_}' =>    PRODUCT,
          '{_time_}' =>       $engine->debug->pagetime('off')
      );
    endif;
    if (defined('__PRINT')):
      $this->standard = array(
          '{_doctype_}' => $engine->doctype(DOCTYPE),
          '{_meta_}' => $engine->meta(),
          '{_doctype_}' => $engine->doctype(DOCTYPE),
          '{_title_}' => $engine->config['title'],
          '{_style_}' => STYLE_DIR . $engine->config['template'],
          '{_copyright_}' => COPYRIGHT . "&nbsp;" . sprintf(_WORKS, PRODUCT),
          '{_product_}' => PRODUCT,
          '{_time_}' => $engine->debug->pagetime('off')
      );
    endif;
    $this->set(null, $this->standard);

    $this->load() ? $this->compile() : die('Not Found Template');
  }

  function set($key, $value) {
    if (is_array($value) && count($value)):
      foreach ($value as $index => $string):
        $this->set($index, $string);
      endforeach;
    else: $this->data[$key] = $value;
    endif;
  }

  function load($config=false) {
    global $engine;
    $tpl = null;
    if (is_array($config)):
      if ($config['module'] != ''): $tpl = TEMPLATES_DIR . "modules/" . $config['module'] . "/" . ($config['prefix'] ? $config['prefix'] . '.' . $config['name'] : $config['name']) . ".tpl.php";
      else: $tpl = TEMPLATES_DIR . "modules/" . ((isset($config['prefix']) && !empty($config['prefix'])) ? $config['prefix'] . '.' . $config['name'] : $config['name']) . ".tpl.php";
      endif;
    else:
      if (defined('_CONTROL')):
        $tpl = TEMPLATES_DIR . $engine->config['template'] . "/admin.php";
      elseif (defined('__DATA')):
        $tpl = TEMPLATES_DIR . $engine->config['template'] . "/data.php";
      elseif (defined('__PRINT')):
        $tpl = TEMPLATES_DIR . $engine->config['template'] . "/print.php";
      else:
        $tpl = TEMPLATES_DIR . $engine->config['template'] . "/template.php";
      endif;
    endif;
    if (!file_exists($tpl)): return false;
    endif;
    switch ($config['mode']):
      case "block":
        $this->block = file_get_contents($tpl);
        break;
      case "panel":
        ob_start();
        require_once($tpl);
        $this->panel = ob_get_contents();
        ob_end_clean();
      case "module":
        ob_start();
        require_once($tpl);
        $this->module = ob_get_contents();
        ob_end_clean();
        break;
      case "template":
      default:
        ob_start();
        require_once($tpl);
        $this->template = ob_get_contents();
        ob_end_clean();
        //$this->template = file_get_contents($tpl);
        break;
    endswitch;
    $this->template = preg_replace('/<!--.*-->/Uis', '', $this->template);
    return true;
  }

  function compile($config=false) {
    if (!empty($this->data)):
      $find = array();
      $replace = array();
      $result = null;
      $tpl = null;
      foreach ($this->data as $key_find => $key_replace):
        $find[] = $key_find;
        $replace[] = $key_replace;
      endforeach;
      switch ($config['mode']):
        case "block":
          $result = str_replace($find, $replace, $this->block);
          break;
        case "panel":
          $result = str_replace($find, $replace, $this->panel);
          break;
        case "module":
          $result = str_replace($find, $replace, $this->module);
          break;
        case "template":
        default:
          $result = str_replace($find, $replace, $this->template);
          break;
      endswitch;
      $find = array();
      $replace = array();
      unset($find);
      unset($replace);
    else:
      $result = $this->template;
    endif;
    $tpl = isset($config['name']) ? $config['name'] : "template";
    isset($this->result[$tpl]) ? $this->result[$tpl].=$result : $this->result[$tpl] = $result;
    $tpl = null;
    $result = null;
    unset($tpl);
    unset($result);
  }

  function clear($config=null) {
    switch ($config['mode']):
      case "panel":
        $this->panel = null;
        break;
      case "block":
        $this->block = null;
        //$this->data=null;
        //$this->data=array();
        break;
      case "module":
        $this->module = null;
        $this->data = null;
        $this->data = array();
        break;
      case "template":
        $this->template = null;
        $this->data = null;
        $this->data = array();
        $this->unload();
        break;
    endswitch;
    if ($config['name']):
      if (isset($this->result[$config['name']])):
        $this->result[$config['name']] = null;
        unset($this->result[$config['name']]);
      endif;
    endif;
  }

  function unload() {
    $this->template = null;
    $this->module = null;
    $this->panel = null;
    $this->block = null;
    $this->backup = null;
    $this->data = null;
    $this->result = null;
  }

  function backup($key='backup') {
    $this->backup[$key]['data'] = $this->data;
    $this->backup[$key]['result'] = $this->result;
    $this->data = null;
    $this->result = null;
  }

  function restore($key='backup') {
    $this->data = $this->backup[$key]['data'];
    $this->result = $this->backup[$key]['result'];
    $this->backup[$key] = null;
    unset($this->backup[$key]);
  }

}

?>

код герерации меню
Код:
/**
   * Вспомлгательная рекурсивная функция для генерации выпадающего меню
   *
   * @param $data=array('name' => '', 'link' => '#', 'image' => '', 'block' => '')
   */
  function items(&$data=array(), $last=null, $level=0) {
    global $engine;
    foreach ($data as $id => $group):
      foreach ($group as $column => $value):
        if ($column === 'image'):
          $value = (is_file(IMAGES_DIR . $this->config['prefix'] . '/' . $value) && file_exists(IMAGES_DIR . $this->config['prefix'] . '/' . $value)) ? WWW.IMAGES_DIR . $this->config['prefix'] . '/' . $value : WWW.IMAGES_DIR . $this->config['prefix'] . '/' . 'default.png';
        endif;
        // если есть сложеный блок менюрекурсивно углубляемся и герерируем меню затем выходя и возвращая управление
        if ($column === 'block'):
          if (is_array($value)):
            $id++;
            $engine->template->set('{class}', ( ($id != $last || $last <= 1 ) ? "class='block'" : "class='block right'"));
            $engine->template->backup($level . $id . $column);
            // вложеных групп нужно хратить ключ шаблона, он является уровнем, тем самым не может пересечся
            $level++; // углубляемся на 1 уровень
            $this->items($value, null, $level);
            $level--; // возвращаемся на свой уровень
            $engine->template->restore($level . $id . $column);
          else:
            // если нет под уровня удалем возможность создания
            $engine->template->set('{class}', '');
          endif;
        endif;
        $this->config['name'] = 'items';
        $engine->template->load($this->config);
        $engine->template->set('{' . $column . '}', $value);
      endforeach;
      $engine->template->compile($this->config);
    endforeach;
    $engine->template->set('{union}', $engine->template->result[$this->config['name']]);
    // очитска старого шаблона
    $engine->template->clear($this->config);
    $this->config['name'] = 'union';
    // избавление от дублирование class=menu в подуровневых меню
    $engine->template->set('{class}', ( ($level != 0 ) ? '' : "class='menu'"));
    $engine->template->load($this->config);
    $engine->template->compile($this->config);
    $data = $engine->template->result[$this->config['name']];
    $engine->template->clear($this->config);
  }

  /**
   * Выпадающее меню
   *
   * @param $data=array('name' => '', 'link' => '#', 'image' => '', 'block' => '')
   *
    $menu = array(
    array('name' => 'Новый 1', 'link' => '#', 'image' => '01.png', 'block' => ''),
    array('name' => 'Новый 2', 'link' => '#', 'image' => '02.png', 'block' => ''),
    array('name' => 'Новый 3', 'link' => '#', 'image' => '03.png', 'block' => ''),
    array('name' => 'Новый 4', 'link' => '#', 'image' => '04.png', 'block' => ''),
    array('name' => 'Новый 5', 'link' => '#', 'image' => '05.png', 'block' =>
    array(
    array('name' => 'Новый 3', 'link' => '#', 'image' => '03.png', 'block' => ''),
    array('name' => 'Новый 4', 'link' => '#', 'image' => '04.png', 'block' => ''),
    array('name' => 'Новый 51', 'link' => '#', 'image' => '05.png', 'block' => '')
    )
    )
    );
   */
  function menu(array $data=array()) {
    global $engine;
    $this->config['module'] = 'element';
    $this->config['prefix'] = 'menu';
    $this->config['name'] = 'items';
    $this->config['mode'] = 'block';
    $this->items($data, count($data));
    $engine->template->set('{menu}', $data);
    $engine->template->clear($this->config);
    $this->config['name'] = 'block';
    $this->config['mode'] = 'module';
    $engine->template->load($this->config);
    $engine->template->compile($this->config);
    return $engine->template->result[$this->config['name']];
    $engine->template->clear($this->config);
  }

меню которое на выходе генерится


есть ли идеи как или что сделать что бы в сейф моде было тоже быстро ?!
 
Конечно есть идея. Сменить этот шаблонизатор на что-то более вменяемое.
 
смарти хорошо но некатит,
я как бы придерживаюсь идеи меньше кода или проще код


смотел всякие шаблонизаторы, этот похож на тот что в DLE, только работает через буффер, остальные не используют замены


возможно стоит заменять через регулярку а не через str_replace
из +
утечек памяти в нем нет,
расход на шаблон не более 200к памяти,
от проца 0.01%

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


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

можно если есть идею по этому именно что чем можно заменить для ускорения замены, загрузки, рекурсивной загрузки или объясните как работает сейф мод что бы для него написать обработку.
----/-/-/----
выяснил причину идея только 1 допиливать
в сейф моде при обращении к фаилу есть проверка и чем меньше обращений тем лучше,
у меня при построении меню около 20 обращений нужно подумать о оптимизации для сокращение вызовов )

но вопрос регурлярка или реплейс остается.


()
 
-
провел ревизию шаблонизатора (в 5-10 раз быстрее, зависит от компа)

теперь без сейф мода 0.052 сек. с сейф модем 0.126 сек.
ввел массив шаблонов в памяти для построения элементов (хранилище)
перед загрузкой проверяю есть ли такой шаблоном в памяти, если есть отдаю его
тут минусую время на чтение и проверку доступа, в рекурсии где около 100 элементов экономит до 50% времени, расход в памяти изменился не сильно +2кб
если же нет загружаю, то делаю дубликат в хранилище.

еще придумал механизм пред загрузки, но пока не реализовал подскажите будет ли он полезен

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

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

if (is_file($file) && file_exists($file):( //то
else: //иначе
endif;
убрал is_file($file) ~10-15% времени исполнения при сейф моде вкл

теперь время геренации в сейф моде 0.089 сек. на этой же тачке ранее
#2 было 0,12 (выкл) стало 0,83 (вкл)
прирост в 10раз
если убрать проверку полностью то еще ~4% но это уже не существенно, тк проверка нужна. вопрос если можно как то иначе проверять в сейф моде наличие фаила подскажите...
 
в общем сам с собой говорить весьма интересно)
в порывах оптимизации кода
было максимальное разнесение функций по тем классам где они должны быть почти полностью избавился от избыточности
поправил шаблонизатор
теперь ненужно делать file_exists($file)
делаю иначе
PHP:
function get($path=null) {
    if (!empty($path)):
      return ( (($this->file = @file_get_contents($path)) && !empty($this->file)) ? $this->file : $path );
    endif;
    return false;
  }
минус этого метода в том что ошибку приходится прятать если она есть, а хотелось бы иначе, ну да ладно это нужно подумать в сторону try cache


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

Код:
<script type="text/javascript">
  <!--
  xform();
  // -->
</script>
<form name='x-form' id='x-form' method='post' action='{action}'>
  [i-task]
  <div id='container'>
    <div class='block'>
      <fieldset>
        [l-login][i-login]<br>
        [l-password][i-password]<br>
        [l-remember][i-remember]<br>
        [i-send]<br>
        [i-lost][i-register]
      </fieldset>
    </div>
    <div class='block'>{%_LOGIN_TEXT_%}</div>
  </div>
</form>
где [***] элементы форм, или html теги с этим проблем нет работает как часы
{*****} это данные тоже проблем нет
{%_LOGIN_TEXT_%} строка языкового пакета тут проблема

сейчас я имею язык вида
Код:
DEFINE('_OPTIONS', "Опции");
DEFINE('_SETTING', "Настройки");
DEFINE('_URL', "ссылка");
DEFINE('_UPLOAD', "загрузить");
но это медленный вариант очень медленный я для этого шаблон должен быть обрамлен в <?php echo'шаблон тут'; ?> и он должен быть добавлен методом include или require
сейчас переписал в массив вида

$language['TUESDAY'] = "Вторник";
$language['WEDNESDAY'] = "Среда";
$language['THURSDAY'] = "Четверг";
$language['FRIDAY'] = "Пятница";
$language['SATURDAY'] = "Суббота";
$language['SUNDAY'] = "Воскресенье";

данные вариант решает иногда проблемы со склонением в языках, тк я это учитываю и есть алгоритм который помогает выявить
но скажем шаблонизатором проходить и делать так
PHP:
  foreach ($engine->LP->strings as $column => $value):
        $engine->template->set('{%' . $column . '%}', $value);
      endforeach;
не хорошо тк строк не мало
в после метода set после идет банальный str_replace
сами представте 1000 значений задать и потом их искать и заменить
беда прям таки (((
если есть идеи или концепт как можно оставить шаблон чистым, и приэтом хранить ленг в массиве то гуд, тк массив после будет серелизован тк так он загружается еще быстрее)
 
Статус
В этой теме нельзя размещать новые ответы.
Назад
Сверху