<?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 => 'fr',
  ),
  'this' => 
  array (
    0 => 'yac.memory-management.php',
    1 => 'Gestion de la m&eacute;moire',
    2 => 'Gestion de la m&eacute;moire',
  ),
  'up' => 
  array (
    0 => 'book.yac.php',
    1 => 'Yac',
  ),
  'prev' => 
  array (
    0 => 'yac.resources.php',
    1 => 'Types de ressources',
  ),
  'next' => 
  array (
    0 => 'yac.constants.php',
    1 => 'Constantes pr&eacute;-d&eacute;finies',
  ),
  'alternatives' => 
  array (
  ),
  'source' => 
  array (
    'lang' => 'fr',
    '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">Gestion de la mémoire</h1>


 <p class="simpara">
  Yac conserve ses données dans deux réservoirs de mémoire partagée
  indépendants, configurés par
  <a href="yac.configuration.php#ini.yac.keys-memory-size" class="link">yac.keys_memory_size</a>
  et
  <a href="yac.configuration.php#ini.yac.values-memory-size" class="link">yac.values_memory_size</a>.
  Ils se remplissent et se libèrent différemment, il est donc utile de savoir
  à quel réservoir se rattache un symptôme avant de toucher à l&#039;un ou l&#039;autre
  réglage.
 </p>

 <div id="yac.memory-layout" class="section">
  <h2 class="title">Ce que contient chaque réservoir</h2>
  <p class="simpara">
   Les deux réservoirs stockent chacun une moitié d&#039;une entrée. Le réservoir
   de clés contient les clés : un slot par clé mise en cache, portant la clé
   elle-même (jusqu&#039;à 48 octets) accompagnée de son hachage, de son TTL, de
   son nombre de succès et de sa date de dernier accès ; la valeur n&#039;est
   référencée que par un pointeur vers le réservoir de valeurs. Le réservoir
   de valeurs contient les valeurs : chaque valeur stockée occupe un bloc
   d&#039;octets sérialisés — sa forme compressée lorsque l&#039;entrée est passée par
   <a href="yac.configuration.php#ini.yac.compress-threshold" class="link">yac.compress_threshold</a>.
  </p>
  <p class="simpara">
   La seule exception est celle des valeurs intégrées : les petits scalaires
   — <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 plupart des entiers (ceux qui tiennent sur
   60 bits signés dans une compilation 64 bits), les chaînes jusqu&#039;à 7 octets
   et les tableaux vides — sont stockés directement dans le slot, dans le
   pointeur qui référencerait autrement un bloc. De telles valeurs
   n&#039;occupent aucune place dans le réservoir de valeurs ; seule leur clé en
   occupe.
  </p>
  <p class="para">
   Pour un dimensionnement approximatif :
   <ul class="itemizedlist">
    <li class="listitem">
     <p class="simpara">
      le réservoir de clés contient environ 8 000 clés par Mo ; dimensionner
      <a href="yac.configuration.php#ini.yac.keys-memory-size" class="link">yac.keys_memory_size</a>
      comme le nombre de clés distinctes divisé par 8 000 par Mo, arrondi au
      supérieur — la valeur par défaut de <code class="literal">8M</code> contient
      environ 64 000 clés ;
     </p>
    </li>
    <li class="listitem">
     <p class="simpara">
      le réservoir de valeurs doit contenir toute valeur susceptible d&#039;être
      encore lue ; dimensionner
      <a href="yac.configuration.php#ini.yac.values-memory-size" class="link">yac.values_memory_size</a>
      comme le nombre de valeurs vivantes multiplié par leur taille
      sérialisée moyenne (après compression), et prévoir environ le double :
      le réservoir est un anneau, et une valeur ne meurt qu&#039;une fois que le
      curseur d&#039;allocation revient l&#039;écraser.
     </p>
    </li>
   </ul>
   Les valeurs intégrées occupent un slot comme toute autre entrée, mais ne
   consomment aucune place dans le réservoir de valeurs : les exclure du
   second calcul.
  </p>
 </div>

 <div id="yac.memory-keys" class="section">
  <h2 class="title">Le réservoir de clés (slots)</h2>
  <p class="simpara">
   <a href="yac.configuration.php#ini.yac.keys-memory-size" class="link">yac.keys_memory_size</a>
   contient une table de slots de taille fixe — la valeur par défaut de
   <code class="literal">8M</code> donne environ 65 536 slots. Chaque clé stockée
   occupe exactement un slot ; ce réservoir plafonne donc le nombre
   d&#039;entrées pouvant exister simultanément. Contrairement au réservoir de
   valeurs, les slots ne sont jamais libérés individuellement. Un slot
   expiré — au delà de son TTL, ou la trace laissée par
   <span class="methodname"><a href="yac.delete.php" class="methodname">Yac::delete()</a></span> — est recyclé gratuitement lorsqu&#039;une
   nouvelle clé en a besoin. Ce n&#039;est que lorsque les quatre slots candidats
   d&#039;un chemin de sondage contiennent des entrées vivantes que l&#039;un d&#039;eux est
   expulsé pour faire de la place : un <code class="literal">kick</code> (le compteur
   <code class="literal">kicks</code> de <span class="methodname"><a href="yac.info.php" class="methodname">Yac::info()</a></span>).
  </p>
  <p class="para">
   L&#039;expulsion choisit uniquement parmi les quatre candidats vivants du
   chemin de sondage en collision :
   <ul class="itemizedlist">
    <li class="listitem">
     <p class="simpara">
      le moins récemment utilisé (l&#039;<code class="literal">atime</code> le plus ancien)
      est expulsé ;
     </p>
    </li>
    <li class="listitem">
     <p class="simpara">
      en cas d&#039;égalité, l&#039;entrée la moins souvent lue, puis la première
      position de sondage.
     </p>
    </li>
   </ul>
  </p>
  <p class="simpara">
   Une confusion fréquente : <code class="literal">slots_used</code> atteignant
   <code class="literal">slots_size</code> n&#039;est <em>pas</em> une
   condition d&#039;erreur. Un cache dont l&#039;ensemble de clés actif est plus grand
   que la table de slots tourne simplement à 100 % d&#039;occupation à
   partir de là, expulsant et réinsérant selon les besoins. La seule chose
   qui dise si le réservoir de clés est correctement dimensionné est le taux
   de succès (<code class="literal">hits / (hits + miss)</code>, calculé sur les
   écarts entre deux instantanés de <span class="methodname"><a href="yac.info.php" class="methodname">Yac::info()</a></span>
   plutôt que sur la moyenne depuis le démarrage). Un compteur
   <code class="literal">kicks</code> élevé ne signifie en lui-même pas qu&#039;il y a un
   problème : la distribution des clés n&#039;est simplement pas uniforme et
   certains chemins de sondage entrent plus souvent en collision. Ce n&#039;est
   que lorsque le taux de succès <em>et</em>
   <code class="literal">kicks</code> sont tous deux mauvais que la table est trop
   petite pour l&#039;ensemble de clés, et le remède est alors un
   <a href="yac.configuration.php#ini.yac.keys-memory-size" class="link">yac.keys_memory_size</a>
   plus grand.
  </p>
  <p class="simpara">
   Seconde conséquence du fait que les slots ne sont jamais libérés : les
   entrées sans TTL (<code class="literal">ttl = 0</code>) qui ne sont plus jamais
   lues continuent d&#039;occuper un slot jusqu&#039;à ce qu&#039;une expulsion les
   désigne. Si une application stocke de grandes quantités de telles données
   à usage unique, leur donner un TTL pour qu&#039;elles expirent et puissent
   être recyclées sans déloger des entrées vivantes, ou dimensionner le
   réservoir de clés pour l&#039;ensemble de clés complet.
  </p>
 </div>

 <div id="yac.memory-values" class="section">
  <h2 class="title">Le réservoir de valeurs (segments)</h2>
  <p class="simpara">
   <a href="yac.configuration.php#ini.yac.values-memory-size" class="link">yac.values_memory_size</a>
   est découpé en segments de 4M chacun, gérés comme des anneaux : les
   écritures avancent un curseur par segment et la place n&#039;est jamais
   libérée entrée par entrée. Lorsqu&#039;une allocation ne tient plus, le
   curseur repart au début d&#039;un segment : un <code class="literal">recycle</code> (le
   compteur <code class="literal">recycles</code> de
   <span class="methodname"><a href="yac.info.php" class="methodname">Yac::info()</a></span>). Un recyclage n&#039;invalide pas le
   segment d&#039;un coup : les valeurs écrasées restent lisibles jusqu&#039;à ce que
   le curseur revenu au début les écrase effectivement, moment à partir
   duquel leurs lectures échouent au contrôle d&#039;intégrité et se
   transforment en défauts de cache.
  </p>
  <p class="simpara">
   Deux tailles comptent pour ce réservoir : le total de
   <a href="yac.configuration.php#ini.yac.values-memory-size" class="link">yac.values_memory_size</a>
   doit contenir l&#039;ensemble actif des valeurs vivantes, et une entrée seule
   ne peut pas dépasser 1 Mo une fois stockée
   (<strong><code><a href="yac.constants.php#constant.yac-max-raw-compressed-len">YAC_MAX_RAW_COMPRESSED_LEN</a></code></strong>). Les valeurs plus
   grandes sont donc toujours compressées avant d&#039;être stockées ; une valeur
   qui ne peut pas descendre en dessous de 1 Mo — le plus souvent parce
   qu&#039;il s&#039;agit de données aléatoires — est rejetée et incrémente le
   compteur <code class="literal">fails</code>. La limite absolue sur la valeur
   elle-même est bien plus haute : les valeurs sérialisées au delà de 64 Mo
   (<strong><code><a href="yac.constants.php#constant.yac-max-value-raw-len">YAC_MAX_VALUE_RAW_LEN</a></code></strong>, soit
   <code class="literal">(1 &lt;&lt; 26) - 1</code> octets) sont rejetées d&#039;emblée.
  </p>
 </div>

 <div id="yac.memory-tuning" class="section">
  <h2 class="title">Dimensionner et surveiller</h2>
  <p class="simpara">
   Commencer avec les valeurs par défaut et surveiller les compteurs de
   <span class="methodname"><a href="yac.info.php" class="methodname">Yac::info()</a></span> : ils s&#039;accumulent depuis
   <code class="literal">start_time</code>, il faut donc comparer deux instantanés
   pris à quelque temps d&#039;intervalle :
  </p>
  <ul class="itemizedlist">
   <li class="listitem">
    <p class="simpara">
     taux de succès sain (disons &gt;= 90 %) : le cache va bien,
     il n&#039;y a rien à faire, quels que soient les autres compteurs ;
    </p>
   </li>
   <li class="listitem">
    <p class="simpara">
     taux de succès faible et <code class="literal">kicks</code> en hausse : le
     réservoir de clés est trop petit pour l&#039;ensemble de clés — des entrées
     vivantes sont expulsées avant d&#039;avoir été relues. Augmenter
     <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> fréquents : c&#039;est un vrai problème, pas un
     compteur anodin. Un recyclage signifie que l&#039;allocateur de valeurs est
     revenu au début et s&#039;apprête à écraser des entrées — tout ce qui est
     écrasé meurt avant d&#039;avoir pu être relu, les octets dépensés à le
     stocker sont donc perdus et le taux de succès en souffre. Le réservoir
     de valeurs est trop petit pour le volume de données vivantes. Par ordre
     d&#039;impact :
     <ul class="itemizedlist">
      <li class="listitem">
       <p class="simpara">
        donner un TTL aux entrées. Les valeurs écrites avec
        <code class="literal">ttl = 0</code> restent vivantes indéfiniment, elles
        continuent donc d&#039;occuper le réservoir et forcent le curseur à
        reboucler plus tôt. Un TTL borne la durée de vie de chaque entrée et
        réduit l&#039;ensemble actif que le réservoir doit contenir ;
       </p>
      </li>
      <li class="listitem">
       <p class="simpara">
        augmenter
        <a href="yac.configuration.php#ini.yac.values-memory-size" class="link">yac.values_memory_size</a>
        pour que le réservoir contienne tout l&#039;ensemble des valeurs vivantes
        (penser à prévoir environ le double de l&#039;empreinte vivante : une
        valeur ne meurt qu&#039;une fois que le curseur revient l&#039;écraser) ;
       </p>
      </li>
      <li class="listitem">
       <p class="simpara">
        stocker moins par entrée : baisser
        <a href="yac.configuration.php#ini.yac.compress-threshold" class="link">yac.compress_threshold</a>
        s&#039;il est réglé au dessus du minimum de <code class="literal">1024</code>, afin
        que les grandes charges utiles soient compressées, et élaguer les
        valeurs qui n&#039;ont pas besoin d&#039;être mises en cache en entier ;
       </p>
      </li>
     </ul>
    </p>
   </li>
   <li class="listitem">
    <p class="simpara">
     <code class="literal">fails</code> en hausse : des valeurs qui n&#039;ont pas pu être
     stockées, le plus souvent une valeur seule dépassant la limite de 1 Mo
     par entrée stockée même après compression — découper la valeur.
    </p>
   </li>
  </ul>
 </div>

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