<?php
include_once $_SERVER['DOCUMENT_ROOT'] . '/include/shared-manual.inc';
$TOC = array();
$TOC_DEPRECATED = array();
$PARENTS = array();
include_once dirname(__FILE__) ."/toc/book.yac.inc";
$setup = array (
  'home' => 
  array (
    0 => 'index.php',
    1 => 'PHP Manual',
  ),
  'head' => 
  array (
    0 => 'UTF-8',
    1 => 'tr',
  ),
  'this' => 
  array (
    0 => 'yac.memory-management.php',
    1 => 'Memory Management',
    2 => 'Memory Management',
  ),
  'up' => 
  array (
    0 => 'book.yac.php',
    1 => 'Yac',
  ),
  'prev' => 
  array (
    0 => 'yac.resources.php',
    1 => '&Ouml;zkaynak T&uuml;rleri',
  ),
  'next' => 
  array (
    0 => 'yac.constants.php',
    1 => '&Ouml;ntanımlı Sabitler',
  ),
  'alternatives' => 
  array (
  ),
  'source' => 
  array (
    'lang' => 'en',
    'path' => 'reference/yac/memory.xml',
  ),
  'history' => 
  array (
  ),
);
$setup["toc"] = $TOC;
$setup["toc_deprecated"] = $TOC_DEPRECATED;
$setup["parents"] = $PARENTS;
manual_setup($setup);

contributors($setup);

?>
<div id="yac.memory-management" class="chapter">
 <h1 class="title">Memory Management</h1>


 <p class="simpara">
  Yac keeps its data in two independent shared-memory pools, configured
  with
  <a href="yac.configuration.php#ini.yac.keys-memory-size" class="link">yac.keys_memory_size</a>
  and
  <a href="yac.configuration.php#ini.yac.values-memory-size" class="link">yac.values_memory_size</a>.
  They fill up and free up in different ways, so it helps to know which
  pool a symptom belongs to before touching either knob.
 </p>

 <div id="yac.memory-layout" class="section">
  <h2 class="title">What Each Pool Holds</h2>
  <p class="simpara">
   The two pools store different halves of an entry. The key pool holds
   the keys: one slot per cached key, carrying the key itself (up to 48
   bytes) together with its hash, TTL, hit count and last-access time;
   the value is only referenced through a pointer to the value pool. The
   value pool holds the values: every stored value occupies one block of
   serialized bytes — the compressed form when the entry went through
   <a href="yac.configuration.php#ini.yac.compress-threshold" class="link">yac.compress_threshold</a>.
  </p>
  <p class="simpara">
   The one exception is embedded values: tiny scalars — <strong><code><a href="reserved.constants.php#constant.null">null</a></code></strong>,
   <strong><code><a href="reserved.constants.php#constant.true">true</a></code></strong>, <strong><code><a href="reserved.constants.php#constant.false">false</a></code></strong>, most integers (those that fit in 60 signed bits on
   64-bit builds), strings of up to 7 bytes and empty arrays — are
   stored directly inside the slot, in the pointer that would otherwise
   reference a block. Such values occupy no space in the value pool at
   all; only their key does.
  </p>
  <p class="para">
   As a rough sizing guide:
   <ul class="itemizedlist">
    <li class="listitem">
     <p class="simpara">
      the key pool holds around 8,000 keys per MB, so size
      <a href="yac.configuration.php#ini.yac.keys-memory-size" class="link">yac.keys_memory_size</a>
      as the number of distinct keys divided by 8,000 per MB, rounded up
      — the default <code class="literal">8M</code> holds around 64,000 keys;
     </p>
    </li>
    <li class="listitem">
     <p class="simpara">
      the value pool must hold every value that may still be read, so
      size
      <a href="yac.configuration.php#ini.yac.values-memory-size" class="link">yac.values_memory_size</a>
      as the number of live values times their average serialized size
      (after compression), and allow roughly twice that: the pool is a
      ring, and a value only dies once the allocator cursor comes back
      around to overwrite it.
     </p>
    </li>
   </ul>
   Embedded values occupy a slot like any other entry, but consume no
   space in the value pool, so leave them out of the second calculation.
  </p>
 </div>

 <div id="yac.memory-keys" class="section">
  <h2 class="title">The key pool (slots)</h2>
  <p class="simpara">
   <a href="yac.configuration.php#ini.yac.keys-memory-size" class="link">yac.keys_memory_size</a>
   holds a fixed-size table of slots — the default of
   <code class="literal">8M</code> gives around 65,536 slots. Each
   stored key occupies exactly one slot, so this pool caps the number
   of entries that can exist at once; unlike the value pool, slots are
   never individually freed. An expired slot — one past its TTL, or the
   tombstone left by <span class="methodname"><a href="yac.delete.php" class="methodname">Yac::delete()</a></span> — is recycled
   for free when a new key needs it. Only when all four candidate slots
   of a probe path hold live entries is one of them evicted to make
   room — one <code class="literal">kick</code> (the
   <code class="literal">kicks</code> counter of <span class="methodname"><a href="yac.info.php" class="methodname">Yac::info()</a></span>).
  </p>
  <p class="para">
   The eviction picks among the four live candidates of the colliding
   probe path only:
   <ul class="itemizedlist">
    <li class="listitem">
     <p class="simpara">
      the least recently used one (the oldest
      <code class="literal">atime</code>) is evicted;
     </p>
    </li>
    <li class="listitem">
     <p class="simpara">
      on a tie the least-hit entry, then the earliest probe position.
     </p>
    </li>
   </ul>
  </p>
  <p class="simpara">
   A common point of confusion: <code class="literal">slots_used</code> reaching
   <code class="literal">slots_size</code> is <em>not</em> an error
   condition. A cache whose working set of keys is larger than the slot
   table simply runs at 100% occupancy from then on, evicting and
   re-inserting as needed. The only thing that says whether the key
   pool is sized correctly is the hit rate
   (<code class="literal">hits / (hits + miss)</code>, computed over the deltas
   between two <span class="methodname"><a href="yac.info.php" class="methodname">Yac::info()</a></span> snapshots rather than
   the lifetime average). A high <code class="literal">kicks</code> count on its
   own means nothing is wrong — the key distribution is simply not
   uniform and some probe paths collide more than others. Only when the
   hit rate <em>and</em> <code class="literal">kicks</code> are both
   bad is the table too small for the key set, and the remedy is a
   bigger <a href="yac.configuration.php#ini.yac.keys-memory-size" class="link">yac.keys_memory_size</a>.
  </p>
  <p class="simpara">
   A second consequence of slots never being freed: entries with no TTL
   (<code class="literal">ttl = 0</code>) that are never read again keep occupying
   a slot until an eviction happens to pick them. If an application
   stores large amounts of such one-shot data, give those entries a TTL
   so they expire and can be recycled without displacing live entries,
   or size the key pool for the full key set.
  </p>
 </div>

 <div id="yac.memory-values" class="section">
  <h2 class="title">The value pool (segments)</h2>
  <p class="simpara">
   <a href="yac.configuration.php#ini.yac.values-memory-size" class="link">yac.values_memory_size</a>
   is split into segments of 4M each, managed as rings: writes advance a
   per-segment cursor and space is never freed per entry. When an
   allocation no longer fits, the cursor wraps back to the start of a
   segment — one <code class="literal">recycle</code> (the
   <code class="literal">recycles</code> counter of
   <span class="methodname"><a href="yac.info.php" class="methodname">Yac::info()</a></span>). A recycle does not invalidate
   the segment at once: overwritten values stay readable until the
   wrapped cursor actually overwrites them, at which point their reads
   fail the integrity guard and turn into misses.
  </p>
  <p class="simpara">
   Two sizes matter for this pool: the total
   <a href="yac.configuration.php#ini.yac.values-memory-size" class="link">yac.values_memory_size</a>
   must hold the working set of live values, and a single entry can
   hold at most 1 MB as stored
   (<strong><code><a href="yac.constants.php#constant.yac-max-raw-compressed-len">YAC_MAX_RAW_COMPRESSED_LEN</a></code></strong>). Values larger
   than that are therefore always compressed before being stored; a
   value that cannot shrink below 1 MB — most often because it is
   random data — is rejected and bumps the
   <code class="literal">fails</code> counter. The absolute size limit on the
   value itself is much higher: serialized values above 64 MB
   (<strong><code><a href="yac.constants.php#constant.yac-max-value-raw-len">YAC_MAX_VALUE_RAW_LEN</a></code></strong>, that is
   <code class="literal">(1 &lt;&lt; 26) - 1</code> bytes) are rejected
   outright.
  </p>
 </div>

 <div id="yac.memory-tuning" class="section">
  <h2 class="title">Sizing and what to watch</h2>
  <p class="simpara">
   Start with the defaults and watch the counters of
   <span class="methodname"><a href="yac.info.php" class="methodname">Yac::info()</a></span> — they accumulate from
   <code class="literal">start_time</code>, so compare two snapshots taken some
   time apart:
  </p>
  <ul class="itemizedlist">
   <li class="listitem">
    <p class="simpara">
     hit rate healthy (say &gt;= 90%): the cache is fine; nothing to do,
     whatever the other counters show;
    </p>
   </li>
   <li class="listitem">
    <p class="simpara">
     hit rate low and <code class="literal">kicks</code> climbing: the key pool is
     too small for the key set — live entries get evicted before they are
     re-read. Raise
     <a href="yac.configuration.php#ini.yac.keys-memory-size" class="link">yac.keys_memory_size</a>;
    </p>
   </li>
   <li class="listitem">
    <p class="para">
     <code class="literal">recycles</code> frequent: this is a real problem, not a
     benign counter. A recycle means the value allocator has wrapped and is
     about to overwrite entries — anything overwritten dies before it could
     be re-read, so the bytes spent storing it were wasted and the hit rate
     suffers. The value pool is too small for the volume of live data. In
     order of impact:
     <ul class="itemizedlist">
      <li class="listitem">
       <p class="simpara">
        give entries a TTL. Values written with <code class="literal">ttl = 0</code>
        stay live forever, so they keep occupying the pool and force the
        cursor to wrap sooner. A TTL bounds how long each entry may live,
        shrinking the live working set the pool has to hold;
       </p>
      </li>
      <li class="listitem">
       <p class="simpara">
        raise
        <a href="yac.configuration.php#ini.yac.values-memory-size" class="link">yac.values_memory_size</a>
        so the pool holds the whole live value set (remember to budget
        roughly twice the live footprint — a value only dies once the
        cursor comes back around to overwrite it);
       </p>
      </li>
      <li class="listitem">
       <p class="simpara">
        store less per entry: lower
        <a href="yac.configuration.php#ini.yac.compress-threshold" class="link">yac.compress_threshold</a>
        if it is set above the <code class="literal">1024</code> minimum, so large
        payloads are compressed, and trim values that do not need to be
        cached in full;
       </p>
      </li>
     </ul>
    </p>
   </li>
   <li class="listitem">
    <p class="simpara">
     <code class="literal">fails</code> growing: values that could not be stored,
     most often a single value larger than the 1 MB stored-size limit
     even after compression — split the value.
    </p>
   </li>
  </ul>
 </div>

</div>
<?php manual_footer($setup); ?>