Babel : fragmentation mémoire

Symptômes

Des allocations mémoire peuvent échouer alors qu'il y a suffisamment de mémoire disponible.

Cause

Le système d'exploitation tournant sur les noeuds de calcul de Babel (appellé CNK pour Compute Node Kernel) est très simple pour venir perturber le moins possible les processus s'exécutant sur la Blue Gene/P et pour être le plus rapide possible.

Le gestionnaire mémoire du CNK est malheureusement très basique et n'est pas capable de gérer le phénomène de la fragmentation mémoire.

La mémoire peut se fragmenter lorsqu'une application fait des allocations et désallocations dynamiques répétées. Par exemple, si vous allouez un certain nombre de petits tableaux en remplissant la mémoire, puis que vous en désallouez certains, des trous vont apparaître. Si vous tentez ensuite de faire une nouvelle allocation, le système d'exploitation va essayer de trouver un emplacement libre suffisamment grand pour y mettre votre nouveau tableau. Si il n'en trouve pas (car votre tableau est plus grand que le plus grand trou), il ne pourra pas l'allouer même si l'espace total disponible est largement suffisant. Un système d'exploitation plus avancé est capable d'allouer des blocs non-contigus en mémoire ou de la défragmenter en déplaçant des blocs. L'approche suivie pour la gestion de la mémoire sur Blue Gene/P permet d'avoir des allocations/désallocations très rapides mais au détriment de sa souplesse.

Conseils

Voici quelques petits conseils pour essayer d'écrire une application qui ne sera pas impactée par de la fragmentation mémoire :

  • Allouer les tableaux de travail une seule fois si possible au début de l'application.
  • Eviter les allocations/désallocations multiples et répétées.
  • Privilégier des allocations/désallocations de tableaux de taille fixe au cours de l'exécution de votre application.

Si vous ne parvenez pas à vous débarasser de ce phénomène, essayez de gérer vous même une partie de l'espace mémoire. Cela peut être fait en allouant un gros espace et en en distribuant des morceaux en cours d'exécution selon vos besoins. Attention, cette approche peut s'avérer très complexe.

Exemple

Voici un petit programme en C qui illustre le problème de la fragmentation mémoire :

#include <stdlib.h>

#define NBLOCKS 5
#define MB 1024*1024

int main(int argc,char **argv)
{
  int i;
  size_t size;
  char *smallblocks[NBLOCKS], *bigblock;

   Allocate 'small' blocks
   In SMP mode, it will use all memory (2GB)
  size = 400*MB;
  for (i=0;i<NBLOCKS;i++) {
    smallblocks[i] = (char *) calloc(size, sizeof(char));
    if(smallblocks[i]==NULL) {
      printf(''Allocation of smallblocks[%i] failed! '',i);
      break;
    }
  }

   Deallocate 1 smallblock every 2
   After deallocation, we should have 3 free blocks
   of 400MB
  for (i=0;i<NBLOCKS;i+=2) free(smallblocks[i]);

   Allocate big block
   Total freespace should be at least 3*400MB=1200MB
  size = 600*MB;
  bigblock = (char *) calloc(size, sizeof(char));
  if(bigblock==NULL) {
   printf(''Allocation of bigblock failed!n'');
   return EXIT_FAILURE;
  }

  return EXIT_SUCCESS;
}

Si nous l'exécutons en mode SMP sur un seul processus, nous obtenons lors de l'exécution les sorties suivantes :

babel1> bgrun -np 1 -exe ./a.out
llsubmit: Processed command file through Submit Filter: ''/bglocal/loadl/Fidris/llsubmit_exit''.
The job babel1-adm.idris.fr.48827 has been submitted,please wait...
Hit CTRL-C to cancel
Allocation of bigblock failed!
<Mar 11 14:27:43.781365> BE_MPI (ERROR): The error message in the job record is as follows:
<Mar 11 14:27:43.781451> BE_MPI (ERROR):   ''killed by exit(1) on node 0''

Run terminated

L'allocation d'un bloc de mémoire de 600 Mo a échoué alors qu'il y avait au moins 1200 Mo de libre.