<?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 => 'es',
  ),
  'this' => 
  array (
    0 => 'yac.memory-management.php',
    1 => 'Gesti&oacute;n de la memoria',
    2 => 'Gesti&oacute;n de la memoria',
  ),
  'up' => 
  array (
    0 => 'book.yac.php',
    1 => 'Yac',
  ),
  'prev' => 
  array (
    0 => 'yac.resources.php',
    1 => 'Tipos de recursos',
  ),
  'next' => 
  array (
    0 => 'yac.constants.php',
    1 => 'Constantes predefinidas',
  ),
  'alternatives' => 
  array (
  ),
  'source' => 
  array (
    'lang' => 'es',
    '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">Gestión de la memoria</h1>


 <p class="simpara">
  Yac guarda sus datos en dos reservas de memoria compartida independientes,
  configuradas con
  <a href="yac.configuration.php#ini.yac.keys-memory-size" class="link">yac.keys_memory_size</a>
  y
  <a href="yac.configuration.php#ini.yac.values-memory-size" class="link">yac.values_memory_size</a>.
  Se llenan y se liberan de maneras distintas, por lo que conviene saber a qué
  reserva pertenece un síntoma antes de tocar cualquiera de los dos ajustes.
 </p>

 <div id="yac.memory-layout" class="section">
  <h2 class="title">Qué contiene cada reserva</h2>
  <p class="simpara">
   Las dos reservas almacenan mitades distintas de una entrada. La reserva de
   claves contiene las claves: una ranura por clave en caché, que lleva la
   propia clave (hasta 48 bytes) junto con su hash, su TTL, su número de
   aciertos y su fecha de último acceso; el valor solo se referencia mediante
   un puntero a la reserva de valores. La reserva de valores contiene los
   valores: cada valor almacenado ocupa un bloque de bytes serializados, en su
   forma comprimida cuando la entrada ha pasado por
   <a href="yac.configuration.php#ini.yac.compress-threshold" class="link">yac.compress_threshold</a>.
  </p>
  <p class="simpara">
   La única excepción son los valores incrustados: los escalares pequeños
   (<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>, la mayoría de los enteros —los que caben en
   60 bits con signo en compilaciones de 64 bits—, las cadenas de hasta
   7 bytes y los arrays vacíos) se almacenan directamente dentro de la ranura,
   en el puntero que de otro modo referenciaría un bloque. Esos valores no
   ocupan espacio alguno en la reserva de valores; solo lo hace su clave.
  </p>
  <p class="para">
   Como guía aproximada de dimensionamiento:
   <ul class="itemizedlist">
    <li class="listitem">
     <p class="simpara">
      la reserva de claves contiene unas 8 000 claves por MB, así que hay que
      dimensionar
      <a href="yac.configuration.php#ini.yac.keys-memory-size" class="link">yac.keys_memory_size</a>
      como el número de claves distintas dividido entre 8 000 por MB,
      redondeando al entero superior: el valor predeterminado de
      <code class="literal">8M</code> contiene unas 64 000 claves;
     </p>
    </li>
    <li class="listitem">
     <p class="simpara">
      la reserva de valores debe contener todos los valores que todavía puedan
      leerse, así que hay que dimensionar
      <a href="yac.configuration.php#ini.yac.values-memory-size" class="link">yac.values_memory_size</a>
      como el número de valores vivos multiplicado por su tamaño serializado
      medio (después de la compresión), y prever aproximadamente el doble: la
      reserva es un anillo, y un valor no muere hasta que el cursor del
      asignador vuelve a pasar por encima de él.
     </p>
    </li>
   </ul>
   Los valores incrustados ocupan una ranura como cualquier otra entrada, pero
   no consumen espacio en la reserva de valores, así que hay que dejarlos
   fuera del segundo cálculo.
  </p>
 </div>

 <div id="yac.memory-keys" class="section">
  <h2 class="title">La reserva de claves (ranuras)</h2>
  <p class="simpara">
   <a href="yac.configuration.php#ini.yac.keys-memory-size" class="link">yac.keys_memory_size</a>
   contiene una tabla de ranuras de tamaño fijo: el valor predeterminado de
   <code class="literal">8M</code> proporciona unas 65 536 ranuras. Cada clave
   almacenada ocupa exactamente una ranura, de modo que esta reserva limita el
   número de entradas que pueden existir a la vez; a diferencia de la reserva
   de valores, las ranuras nunca se liberan individualmente. Una ranura
   caducada —más allá de su TTL, o el marcador de borrado que deja
   <span class="methodname"><a href="yac.delete.php" class="methodname">Yac::delete()</a></span>— se recicla sin coste cuando una clave
   nueva la necesita. Solo cuando las cuatro ranuras candidatas de un camino
   de sondeo contienen entradas vivas se expulsa una de ellas para hacer
   sitio: un <code class="literal">kick</code> (el contador
   <code class="literal">kicks</code> de <span class="methodname"><a href="yac.info.php" class="methodname">Yac::info()</a></span>).
  </p>
  <p class="para">
   La expulsión elige únicamente entre las cuatro candidatas vivas del camino
   de sondeo en colisión:
   <ul class="itemizedlist">
    <li class="listitem">
     <p class="simpara">
      se expulsa la usada menos recientemente (el <code class="literal">atime</code> más
      antiguo);
     </p>
    </li>
    <li class="listitem">
     <p class="simpara">
      en caso de empate, la entrada con menos aciertos y, después, la primera
      posición de sondeo.
     </p>
    </li>
   </ul>
  </p>
  <p class="simpara">
   Un punto de confusión frecuente: que <code class="literal">slots_used</code> alcance
   <code class="literal">slots_size</code> <em>no</em> es una condición de
   error. Una caché cuyo conjunto de trabajo de claves sea mayor que la tabla
   de ranuras simplemente funciona al 100 % de ocupación a partir de ese
   momento, expulsando y reinsertando según haga falta. Lo único que indica si
   la reserva de claves está bien dimensionada es la tasa de aciertos
   (<code class="literal">hits / (hits + miss)</code>, calculada sobre las diferencias
   entre dos instantáneas de <span class="methodname"><a href="yac.info.php" class="methodname">Yac::info()</a></span> y no sobre la
   media desde el arranque). Un contador <code class="literal">kicks</code> alto no
   significa por sí mismo que haya un problema: sencillamente la distribución
   de las claves no es uniforme y algunos caminos de sondeo colisionan más que
   otros. Solo cuando la tasa de aciertos <em>y</em>
   <code class="literal">kicks</code> son ambos malos la tabla es demasiado pequeña para
   el conjunto de claves, y el remedio es un
   <a href="yac.configuration.php#ini.yac.keys-memory-size" class="link">yac.keys_memory_size</a> mayor.
  </p>
  <p class="simpara">
   Una segunda consecuencia de que las ranuras nunca se liberen: las entradas
   sin TTL (<code class="literal">ttl = 0</code>) que ya no vuelven a leerse siguen
   ocupando una ranura hasta que una expulsión da la casualidad de elegirlas.
   Si una aplicación almacena grandes cantidades de datos de un solo uso,
   conviene darles un TTL para que caduquen y puedan reciclarse sin desplazar
   entradas vivas, o dimensionar la reserva de claves para el conjunto
   completo de claves.
  </p>
 </div>

 <div id="yac.memory-values" class="section">
  <h2 class="title">La reserva de valores (segmentos)</h2>
  <p class="simpara">
   <a href="yac.configuration.php#ini.yac.values-memory-size" class="link">yac.values_memory_size</a>
   se divide en segmentos de 4M cada uno, gestionados como anillos: las
   escrituras hacen avanzar un cursor por segmento y el espacio nunca se
   libera por entrada. Cuando una asignación ya no cabe, el cursor vuelve al
   principio de un segmento: un <code class="literal">recycle</code> (el contador
   <code class="literal">recycles</code> de <span class="methodname"><a href="yac.info.php" class="methodname">Yac::info()</a></span>). Un
   reciclado no invalida el segmento de inmediato: los valores sobrescritos
   siguen siendo legibles hasta que el cursor que ha dado la vuelta los
   sobrescribe realmente, momento en el que sus lecturas no superan el control
   de integridad y se convierten en fallos de caché.
  </p>
  <p class="simpara">
   Para esta reserva importan dos tamaños: el total de
   <a href="yac.configuration.php#ini.yac.values-memory-size" class="link">yac.values_memory_size</a>
   debe contener el conjunto de trabajo de los valores vivos, y una única
   entrada puede ocupar como mucho 1 MB una vez almacenada
   (<strong><code><a href="yac.constants.php#constant.yac-max-raw-compressed-len">YAC_MAX_RAW_COMPRESSED_LEN</a></code></strong>). Por eso los valores
   mayores se comprimen siempre antes de almacenarse; un valor que no puede
   reducirse por debajo de 1 MB —lo más habitual, porque son datos
   aleatorios— es rechazado e incrementa el contador
   <code class="literal">fails</code>. El límite absoluto sobre el valor en sí es mucho
   más alto: los valores serializados por encima de 64 MB
   (<strong><code><a href="yac.constants.php#constant.yac-max-value-raw-len">YAC_MAX_VALUE_RAW_LEN</a></code></strong>, es decir
   <code class="literal">(1 &lt;&lt; 26) - 1</code> bytes) se rechazan de plano.
  </p>
 </div>

 <div id="yac.memory-tuning" class="section">
  <h2 class="title">Dimensionamiento y qué vigilar</h2>
  <p class="simpara">
   Conviene empezar con los valores predeterminados y vigilar los contadores de
   <span class="methodname"><a href="yac.info.php" class="methodname">Yac::info()</a></span>: se acumulan desde
   <code class="literal">start_time</code>, así que hay que comparar dos instantáneas
   tomadas con cierta separación en el tiempo:
  </p>
  <ul class="itemizedlist">
   <li class="listitem">
    <p class="simpara">
     tasa de aciertos saludable (por ejemplo &gt;= 90 %): la caché está bien;
     no hay nada que hacer, sea lo que sea que muestren los demás contadores;
    </p>
   </li>
   <li class="listitem">
    <p class="simpara">
     tasa de aciertos baja y <code class="literal">kicks</code> en aumento: la reserva de
     claves es demasiado pequeña para el conjunto de claves, las entradas vivas
     se expulsan antes de volver a leerse. Hay que aumentar
     <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> frecuentes: esto sí es un problema real, no un
     contador inofensivo. Un reciclado significa que el asignador de valores ha
     dado la vuelta y está a punto de sobrescribir entradas: todo lo
     sobrescrito muere antes de poder volver a leerse, de modo que los bytes
     gastados en almacenarlo se han desperdiciado y la tasa de aciertos se
     resiente. La reserva de valores es demasiado pequeña para el volumen de
     datos vivos. Por orden de impacto:
     <ul class="itemizedlist">
      <li class="listitem">
       <p class="simpara">
        hay que dar un TTL a las entradas. Los valores escritos con
        <code class="literal">ttl = 0</code> siguen vivos para siempre, de modo que
        ocupan la reserva y obligan al cursor a dar la vuelta antes. Un TTL
        limita cuánto puede vivir cada entrada y reduce el conjunto de trabajo
        vivo que la reserva debe contener;
       </p>
      </li>
      <li class="listitem">
       <p class="simpara">
        hay que aumentar
        <a href="yac.configuration.php#ini.yac.values-memory-size" class="link">yac.values_memory_size</a>
        para que la reserva contenga todo el conjunto de valores vivos (hay que
        recordar prever aproximadamente el doble de la huella viva: un valor no
        muere hasta que el cursor vuelve a pasar por encima de él);
       </p>
      </li>
      <li class="listitem">
       <p class="simpara">
        hay que almacenar menos por entrada y bajar
        <a href="yac.configuration.php#ini.yac.compress-threshold" class="link">yac.compress_threshold</a>
        si está por encima del mínimo de <code class="literal">1024</code>, para que las
        cargas útiles grandes se compriman, y recortar los valores que no
        necesitan almacenarse por completo;
       </p>
      </li>
     </ul>
    </p>
   </li>
   <li class="listitem">
    <p class="simpara">
     <code class="literal">fails</code> en aumento: valores que no han podido
     almacenarse, casi siempre un único valor mayor que el límite de 1 MB de
     tamaño almacenado incluso después de comprimirlo; hay que dividir el
     valor.
    </p>
   </li>
  </ul>
 </div>

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