<?php
include_once $_SERVER['DOCUMENT_ROOT'] . '/include/shared-manual.inc';
$TOC = array();
$TOC_DEPRECATED = array();
$PARENTS = array();
include_once dirname(__FILE__) ."/toc/features.gc.inc";
$setup = array (
  'home' => 
  array (
    0 => 'index.php',
    1 => 'PHP Manual',
  ),
  'head' => 
  array (
    0 => 'UTF-8',
    1 => 'ru',
  ),
  'this' => 
  array (
    0 => 'features.gc.performance-considerations.php',
    1 => 'Вопросы производительности',
    2 => 'Вопросы производительности',
  ),
  'up' => 
  array (
    0 => 'features.gc.php',
    1 => 'Сборка мусора',
  ),
  'prev' => 
  array (
    0 => 'features.gc.collecting-cycles.php',
    1 => 'Сбор циклических ссылок',
  ),
  'next' => 
  array (
    0 => 'features.dtrace.php',
    1 => 'Динамическая трассировка DTrace',
  ),
  'alternatives' => 
  array (
  ),
  'source' => 
  array (
    'lang' => 'ru',
    'path' => 'features/gc.xml',
  ),
  'history' => 
  array (
  ),
);
$setup["toc"] = $TOC;
$setup["toc_deprecated"] = $TOC_DEPRECATED;
$setup["parents"] = $PARENTS;
manual_setup($setup);

contributors($setup);

?>
<div id="features.gc.performance-considerations" class="sect1">
   <h2 class="title">Вопросы производительности</h2>
   <p class="para">
    Предыдущий раздел уже упоминал, что простой сбор корней
    незначительно влияет на производительность, но это если сравнивать PHP 5.2 с PHP 5.3.
    Хотя запись корней в буфер по сравнению с отсутствием такой записи
    в PHP 5.2 замедляет работу приложения, другие изменения в работе
    PHP 5.3 во время выполнения кода предотвратили даже проявление этой конкретной потери производительности.
   </p>
   <p class="para">
    Производительность страдает в двух главных областях. Первая область —
    уменьшение размера памяти, которую PHP тратит на запись корней, а вторая —
    задержка во время выполнения кода, когда механизм сборки мусора очищает память.
    Рассмотрим обе проблемы.
   </p>

   <div class="sect2" id="features.gc.performance-considerations.reduced-mem">
    <h3 class="title">Уменьшение размера памяти</h3>
    <p class="para">
     Первая причина появления в языке механизма сборки
     мусора, состоит в уменьшении размера памяти, которую занимает мусор,
     путём очистки переменных с циклическими ссылками в тот момент, когда выполнятся
     предварительные условия. В реализации PHP сборка мусора начинается, как
     только заполняется корневой буфер или при вызове функции
     <span class="function"><a href="function.gc-collect-cycles.php" class="function">gc_collect_cycles()</a></span>.
     График ниже показывает, как скрипт под графиком занимает память
     в PHP 5.2 и PHP 5.3, без учёта памяти, которую занимает сам PHP при запуске.
    </p>
    <p class="para">
     <div class="example" id="example-1">
      <p><strong>Пример #1 Пример использования памяти</strong></p>
      <div class="example-contents">
<div class="phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /><br /></span><span style="color: #007700">class </span><span style="color: #0000BB">Foo<br /></span><span style="color: #007700">{<br />    public </span><span style="color: #0000BB">$var </span><span style="color: #007700">= </span><span style="color: #DD0000">'3.14159265359'</span><span style="color: #007700">;<br />    public </span><span style="color: #0000BB">$self</span><span style="color: #007700">;<br />}<br /><br /></span><span style="color: #0000BB">$baseMemory </span><span style="color: #007700">= </span><span style="color: #0000BB">memory_get_usage</span><span style="color: #007700">();<br /><br />for (</span><span style="color: #0000BB">$i </span><span style="color: #007700">= </span><span style="color: #0000BB">0</span><span style="color: #007700">; </span><span style="color: #0000BB">$i </span><span style="color: #007700">&lt;= </span><span style="color: #0000BB">100000</span><span style="color: #007700">; </span><span style="color: #0000BB">$i</span><span style="color: #007700">++) {<br />    </span><span style="color: #0000BB">$a </span><span style="color: #007700">= new </span><span style="color: #0000BB">Foo</span><span style="color: #007700">();<br />    </span><span style="color: #0000BB">$a</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">self </span><span style="color: #007700">= </span><span style="color: #0000BB">$a</span><span style="color: #007700">;<br />    if (</span><span style="color: #0000BB">$i </span><span style="color: #007700">% </span><span style="color: #0000BB">500 </span><span style="color: #007700">=== </span><span style="color: #0000BB">0</span><span style="color: #007700">) {<br />        echo </span><span style="color: #0000BB">sprintf</span><span style="color: #007700">(</span><span style="color: #DD0000">'%8d: '</span><span style="color: #007700">, </span><span style="color: #0000BB">$i</span><span style="color: #007700">), </span><span style="color: #0000BB">memory_get_usage</span><span style="color: #007700">() - </span><span style="color: #0000BB">$baseMemory</span><span style="color: #007700">, </span><span style="color: #DD0000">"\n"</span><span style="color: #007700">;<br />    }<br />}<br /><br /></span><span style="color: #0000BB">?&gt;</span></span></code></div>
      </div>

      <div class="mediaobject">
       
       <div class="imageobject">
        <img src="images/12f37b1c6963c1c5c18f30495416a197-gc-benchmark.png" alt="Сравнение потребления памяти в PHP 5.2 и PHP 5.3" width="850" height="480" />
       </div>
      </div>
     </div>
    </p>
    <p class="para">
     В этом академическом примере создаётся объект, в котором устанавливается свойство,
     которое указывает на сам объект. Когда в скрипте переменной <var class="varname">$a</var>
     на следующей итерации цикла повторно присваивается значение,
     происходит типичная утечка памяти.
     В примере утекает память для двух zval-контейнеров — контейнера объекта
     и контейнера свойства объекта, — но алгоритм находит только один корень: переменную, которую удалили.
     Как только после 10 000 итераций (если PHP-сборка разрешает только
     10 000 корней) корневой буфер заполняется, срабатывает механизм сборки мусора
     и память, которую занимают эти корни, освобождается.
     Этот процесс хорошо виден на неравномерном графике потребления памяти PHP 5.3:
     после каждых 10 000 итераций график проседает.
     Сам механизм в примере совершает не много работы, потому что
     структура утечек проста. Из графика видно, что максимальное потребление памяти
     в PHP 5.3 составило около 9 МБ, тогда как в PHP 5.2 потребление памяти продолжает расти.
    </p>
   </div>

   <div class="sect2" id="features.gc.performance-considerations.slowdowns">
    <h3 class="title">Замедление работы</h3>
    <p class="para">
     Вторая область, в которой механизм сборки мусора влияет на производительность, —
     потеря времени, которое требуется сборщику мусора для освобождения «утечки» памяти.
     Чтобы понять степень влияния, изменим предыдущий скрипт путём
     добавления количества итераций и удаления промежуточных показателей потребления памяти.
     После изменения скрипт выглядит вот так:
    </p>
    <p class="para">
     <div class="example" id="example-2">
      <p><strong>Пример #2 Влияние на производительность</strong></p>
      <div class="example-contents">
<div class="phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /><br /></span><span style="color: #007700">class </span><span style="color: #0000BB">Foo<br /></span><span style="color: #007700">{<br />    public </span><span style="color: #0000BB">$var </span><span style="color: #007700">= </span><span style="color: #DD0000">'3.14159265359'</span><span style="color: #007700">;<br />    public </span><span style="color: #0000BB">$self</span><span style="color: #007700">;<br />}<br /><br />for (</span><span style="color: #0000BB">$i </span><span style="color: #007700">= </span><span style="color: #0000BB">0</span><span style="color: #007700">; </span><span style="color: #0000BB">$i </span><span style="color: #007700">&lt;= </span><span style="color: #0000BB">1000000</span><span style="color: #007700">; </span><span style="color: #0000BB">$i</span><span style="color: #007700">++) {<br />    </span><span style="color: #0000BB">$a </span><span style="color: #007700">= new </span><span style="color: #0000BB">Foo</span><span style="color: #007700">();<br />    </span><span style="color: #0000BB">$a</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">self </span><span style="color: #007700">= </span><span style="color: #0000BB">$a</span><span style="color: #007700">;<br />}<br /><br />echo </span><span style="color: #0000BB">memory_get_peak_usage</span><span style="color: #007700">(), </span><span style="color: #DD0000">"\n"</span><span style="color: #007700">;<br /><br /></span><span style="color: #0000BB">?&gt;</span></span></code></div>
      </div>

     </div>
    </p>
    <p class="para">
     Запустим скрипт два раза: с включённой опцией
     <a href="info.configuration.php#ini.zend.enable-gc" class="link">zend.enable_gc</a> и без неё.
    </p>
    <p class="para">
     <div class="example" id="example-3">
      <p><strong>Пример #3 Запуск скрипта</strong></p>
      <div class="example-contents">
<div class="shellcode"><pre class="shellcode">time php -dzend.enable_gc=0 -dmemory_limit=-1 -n example2.php
# и
time php -dzend.enable_gc=1 -dmemory_limit=-1 -n example2.php</pre>
</div>
      </div>

     </div>
    </p>
    <p class="para">
     На тестовой машине первая команда выполняется примерно 10.7 секунды,
     а вторая примерно 11.4 секунды. Это примерно на 7 % медленнее. Однако
     максимальное потребление памяти скриптом уменьшилось на 98 % — с 931 до 10 МБ.
     Этот тест производительности не научный и даже не представляет реальное приложение,
     но показывает преимущества в работе с памятью, которые даёт механизм сборки мусора.
     Хорошо то, что замедление скрипта каждый раз составляет одни и те же 7 %,
     тогда как экономия памяти постоянно увеличивается по мере того, как алгоритм
     во время выполнения скрипта обнаруживает всё больше циклических ссылок.
    </p>
   </div>

   <div class="sect2" id="features.gc.performance-considerations.internal-stats">
    <h3 class="title">Внутренняя статистика сборщика мусора</h3>
    <p class="para">
     PHP умеет выдавать больше информации о том, как механизм сборки
     мусора выполняется в PHP. Но для этого потребуется перекомпилировать PHP,
     чтобы включить код теста производительности и сбора данных. До запуска команды
     <code class="literal">./configure</code> с параметрами, которые требуются пользователю,
     потребуется установить для переменной окружения <code class="literal">CFLAGS</code> значение
     <code class="literal">-DGC_BENCH=1</code>. Следующая последовательность должна сработать:
    </p>
    <p class="para">
     <div class="example" id="example-4">
      <p><strong>Пример #4 Пример перекомпиляции PHP для включения теста производительности сборки мусора</strong></p>
      <div class="example-contents">
<div class="shellcode"><pre class="shellcode">export CFLAGS=-DGC_BENCH=1
./config.nice
make clean
make</pre>
</div>
      </div>

     </div>
    </p>
    <p class="para">
     При повторном запуске приведенного примера кода с двоичным файлом PHP, который
     только что создали, после завершения выполнения PHP выведет следующее:
    </p>
    <p class="para">
     <div class="example" id="example-5">
      <p><strong>Пример #5 Статистика сборки мусора</strong></p>
      <div class="example-contents">
<div class="shellcode"><pre class="shellcode">GC Statistics
-------------
Runs:               110
Collected:          2072204
Root buffer length: 0
Root buffer peak:   10000

      Possible            Remove from  Marked
        Root    Buffered     buffer     grey
      --------  --------  -----------  ------
ZVAL   7175487   1491291    1241690   3611871
ZOBJ  28506264   1527980     677581   1025731</pre>
</div>
      </div>

     </div>
    </p>
    <p class="para">
     Самую информативную статистику показывает первый блок. Видно,
     что механизм сборки мусора запускался 110 раз, и суммарно освободил
     больше 2 миллионов записей в памяти. Как только механизм сборки мусора
     сработал хотя бы один раз, показатель пика корневого буфера (Root buffer peak)
     будет равняться 10 000.
    </p>
   </div>

   <div class="sect2" id="features.gc.performance-considerations.conclusion">
    <h3 class="title">Заключение</h3>
    <p class="para">
     Сборщик мусора в PHP вызывает замедление работы только
     во время работы алгоритма сборки циклических ссылок, тогда как
     в стандартных скриптах меньшего размера производительность не падает.
    </p>
    <p class="para">
     Когда механизм сборки циклов все-таки запускается
     для стандартных скриптов, объём памяти, которую экономит механизм,
     разрешает одновременно запускать на сервере большее количество скриптов,
     поскольку в целом скрипты занимают не так много памяти.
    </p>
    <p class="para">
     Преимущества заметнее для скриптов, которые работают долго —
     большие наборы тестов или демоны. Новый механизм существенно сокращает утечки памяти
     для приложений, которые работают с расширением <a href="http://gtk.php.net/" class="link external">&raquo;&nbsp;PHP-GTK</a>,
     которые часто выполняются дольше, чем скрипты для веба.
    </p>
   </div>
  </div><?php manual_footer($setup); ?>