Logo
  Erlang-fr.org  

Présentation du site
Cours Erlang
Articles
Projets
Autour d'Erlang-fr
Liens
 
English Area
Miroir: Documentation Erlang
Miroir: Archives Erlang/OTP
 
Recherche

Logo Process-one
Erlang

Hipe: La compilation native de programmes Erlang

La nouvelle version de l'environnement de développement Erlang inclus désormais Hipe. Hipe (High-Performance Erlang) est à l'origine un projet de l'Université d'Uppsala ayant pour objectif l'amélioration des performances des programmes Erlang. Hipe est un compilateur générant du code natif sur Solaris Sparc et Linux x86.

Téléchargement d'Erlang R8

Le version R8B d'Erlang/OTP est sortie le 17 octobre 2001. Elle est disponible sur le site officiel d'Erlang, Erlang.org, et peut-être téléchargé à l'adresse suivante: http://www.erlang.org/download.html

Compilation

La compilation sous Linux nécessite de lancer les commandes suivantes dans le répertoire racine de l'archive téléchargée, après sa décompression:

./configure --enable-hipe
make
make install
La dernière commande doit être lancée avec les droits d'administrateur de la machine (root).

Le principe

Il faut savoir que le mécanisme de compilation native ne génère pas de fichier directement exécutable, mais intégre simplement le code exécutable dans le fichier .beam qui contient le pseudo-code. Cette approche peut sembler étrange, mais elle est en fait très cohérente. Elle permet de distribuer uniquement les fichiers beam tout en conservant le caractère multiplateforme. Seuls les plate-formes pour lequel le code natif peut être exécuté l'utiliseront. Les autres plateformes utiliseront le pseudo-code contenu dans le fichier beam. L'autre avantage majeur est que le mécanisme de fonctionnement des applications existantes n'est pas perturbé. Grâce à ce mécanisme, on peut même compiler certains modules, voire certaines fonctions dans ces modules, qui collaboreront avec des modules interprétés.

Les premiers tests

Nous allons prendre un exemple extrêment simple pour bien comprendre le mécanisme de compilation.

Nous allons compiler le programme suivant:

-module(test).

-export([go/0]).

go()->
    io:format("~p~n", [calendar:universal_time()]).

Il existe deux méthodes traditionnelles pour compiler ce programme en pseudo-code:

  1. La compilation pseudo-code en ligne de commande:
      [mikl]/home/mikl/erlang-fr.org % erlc test.erl
      [mikl]/home/mikl/erlang-fr.org % ls -l test.beam
      -rw-rw-r--    1 mikl     mikl          504 nov  3 11:21 test.beam
     
  2. La compilation pseudo-code depuis le shell Erlang:
      [mikl]/home/mikl/erlang-fr.org % erl
      Erlang (BEAM) emulator version 5.1 [source] [hipe]
    
      Eshell V5.1  (abort with ^G)
      1> c(test).
      {ok,test}
     
    Le fichier produit est légérement plus petit, probablement car les options de compilation utilisées ne sont pas les mêmes:
      [mikl]/home/mikl/erlang-fr.org % ls -l test.beam
      -rw-rw-r--    1 mikl     mikl          416 nov  3 11:26 test.beam
     

Nous disposons des mêmes possibilités pour la compilation native:

  1. La compilation native en ligne de commande:
      [mikl]/home/mikl/erlang-fr.org % erlc +native test.erl
      [mikl]/home/mikl/erlang-fr.org % ls -l test.beam
      -rw-rw-r--    1 mikl     mikl         2096 nov  3 11:40 test.beam
     
    On remarque que le fichier test.beam est plus gros: Il embarque à la fois la version pseudo-code et la version native.
  2. La compilation native depuis le shell Erlang:
      Erlang (BEAM) emulator version 5.1 [source] [hipe]
    
      Eshell V5.1  (abort with ^G)
      1> c(test,[native]).      
      {ok,test}
     
    Le résultat est la encore incorporé au fichier beam contenant le pseudo-code:
      [mikl]/home/mikl/erlang-fr.org % ls -l test.beam
      -rw-rw-r--    1 mikl     mikl         2008 nov  3 11:51 test.beam
     

Quelques options

La compilation native accepte quelques options permettant d'optimiser plus ou moins le code produit (o1, o2, o3) ou bien d'obtenir des informations sur le processus de compilation (verbose). Voici un exemple d'utilisation de ces options:

  Erlang (BEAM) emulator version 5.1 [source] [hipe]

  Eshell V5.1  (abort with ^G)
  1> c(test, [native,  {hipe,[o3,verbose]}]).
  [HiPE Compiler (v 1.0.0)] Options: [{'O',3},
                                      icode_rename,
                                      {regalloc,coalescing},
                                      use_indexing,
                                      icode_prop,
                                      remove_comments,
                                      trim_frame,
                                      rtl_prop_2,
                                      verbose].
  [HiPE Compiler (v 1.0.0)] Compiling {test,go,0} in 0.32 s
  [HiPE Compiler (v 1.0.0)] Compiling {test,module_info,0} in 0.02 s
  [HiPE Compiler (v 1.0.0)] Compiling {test,module_info,1} in 0.00 s
  [HiPE Compiler (v 1.0.0)] Compiled test in 0.34 s
  [HiPE Compiler (v 1.0.0)] Assembling test in 0.05 s
  {ok,test}
 

Les performances

Quels gains de performance apporte la compilation native ?

Sur le module test.erl, l'exécution de la fonction go() prend 261 micro-secondes en mode interprété:

  Erlang (BEAM) emulator version 5.1 [source] [hipe]

  Eshell V5.1  (abort with ^G)
  1> m(test).
  Module test compiled: Date: November 3 2001, Time: 11.03
  Compiler options:  [v3]
  Object file: /home/mikl/erlang/perso/workshop/test.beam
  Exports: 
           go/0
           module_info/0
           module_info/1
  ok
  6> timer:tc(test,go,[]).
  {{2001,11,3},{11,4,21}}
  {261,ok}
 

Le temps d'exécution est identique sur la version compilée:

  Erlang (BEAM) emulator version 5.1 [source] [hipe]

  Eshell V5.1  (abort with ^G)
  1> m(test).
  Module test compiled: Date: November 3 2001, Time: 11.06
  Compiler options:  [v3,native]
  Object file: /home/mikl/erlang/perso/workshop/test.beam
  Exports: 
           go/0
           module_info/0
           module_info/1
           module_info/0
           module_info/1
  ok
  6> timer:tc(test,go,[]).
  {{2001,11,3},{11,9,34}}
  {242,ok}
 

Ce test n'est toutefois absoluement pas signicatif, car le temps d'exécution total est beaucoup trop faible pour être représentatif. La moindre variation d'activité de la machine peut faire changer le résultat du simple au double.

Nous allons donc faire le test sur le calcul de la fonction d'ackermann, tel qu'il est réalisé dans le benchmark «shoutout»:

%%% -*- mode: erlang -*-
%%% $Id: ackermann.erlang,v 1.6 2000/10/07 08:41:43 doug Exp $
%%% http://www.bagley.org/~doug/shootout/

-module(ackermann).
-export([main/1]).

main() -> main(['1']).
main([Arg]) ->
    Num = list_to_integer(atom_to_list(Arg)),
    io:fwrite("Ack(3,~w): ~w\n", [Num, ack(3, Num)]),
    ok.
    %%halt(0).

ack(0, N) -> N+1;
ack(M, 0) -> ack(M-1, 1);
ack(M, N) -> ack(M-1, ack(M, N-1)).
 

Le temps d'exécution de la fonction en mode interprété est de 7,8 secondes (paramètre 10):

  Erlang (BEAM) emulator version 5.1 [source] [hipe]

  Eshell V5.1  (abort with ^G)
  1> c(ackermann).
  ./ackermann.erl:18: Warning: function main/0 is unused
  {ok,ackermann}
  2> timer:tc(ackermann,main,[['10']]).
  Ack(3,10): 8189
  {7783410,ok}
 

Le temps d'exécution est réduit de 3,6 fois avec la compilation native:

  Erlang (BEAM) emulator version 5.1 [source] [hipe]

  Eshell V5.1  (abort with ^G)
  1> c(ackermann,[native,{hipe,o3}]).
  ./ackermann.erl:18: Warning: function main/0 is unused
  {ok,ackermann}
  2> timer:tc(ackermann,main,[['10']]).
  Ack(3,10): 8189
  {2146980,ok}
 

La compilation native permet donc d'améliorer les performances d'un code Erlang intensif en traitement. D'autres tests monte cependant que l'amélioration est négligeable sur du code utilisant des échanges sockets par exemple. C'est le cas par exemple avec le code Echo Client/serveur du benchmark shoutout.

Les effets de bord

La compilation native induit cependant quelques effets de bord. Les informations de déboguage sur du code natif sont quasi inexistante. Je recommande donc de ne pas utiliser la compilation native en phase de développement, mais pour le déploiement du logiciel.

D'autre part, si vous utilisez les informations de débogguage pour certains mécanismes de tolérance aux pannes dans vos programmes, la compilation native peut avoir des conséquences sur le fonctionnement même du programme. Je recommande donc de passer votre jeu de test sur le code compilé nativement avant tout déploiement.

Historique de ce document

Version initiale (2001-11-03): Mickaël Rémond.

Pour tout commentaire: erlang@erlang-fr.org

Dernière modification: 2005-11-11 18:48:4