Etude et comparaison des moyens d'accèder à R en Java

Introduction

Ce document a pour but de comparer les différentes solutions envisageables pour l'utilisation de R en Java.

R est un outil libre de calculs et de création de graphiques liés aux statistiques. Il présente l'avantage non négligeable d'être optimisé pour bon nombre de calculs. Cependant, R n'est pas écrit en Java et ne permet donc pas d'être directement utilisé dans une application Java.

Heureusement, R est un projet très modulaire et permet par le biais d'extensions d'ouvrir son moteur à d'autres appplications. En Java, deux possibilités s'offrent à nous :

  • Accès par le réseau : L'application envoie des requêtes par le réseau à une extension de R faisant office de serveur, laquelle renvoie par la suite les résultats obtenus.

  • Accès par une librairie JNI : Il s'agit d'écrire du code en un langage autre que Java qui sera compilé et excécuté par la machine plutôt qu'interprété. Grâce à JNI, il est ensuite possible d'appeler ce code depuis une application Java.

Le présent document va donc comparer ces deux solutions afin de déterminer laquelle est la plus adaptée. Afin d'effectuer une comparaison plus pertinente, ces solutions seront comparées, lorsque c'est possible, à l'utilisation de R seul (sans Java) et Java seul (sans R).

Résultats attendus

Chacune des solutions a ses avantages et ses inconvénients qui entreront dans la décision finale. Avant même de commencer, la nature même des solutions suggère certains résultats qu'il faudra vérifier :

  • Les temps de réponse obtenus en R pur seront inférieurs aux solutions réseau et JNI. Le contraire serait étonnant dans la mesure où l'utilisation de R pur est la seule solution n'impliquant pas de technologie tierce.

  • Les appels JNI devraient prendre moins de temps que les appels réseau. L'utilisation des interfaces réseau est reconnue est informatique pour être un point qui ralenti souvent les applications.

  • Certains calculs simples effectués en Java pur pourraient parfois s'avérer plus rapide. Cependant, des calculs plus complexes comme des calculs matriciels devraient faire ressortir l'avantage de R.

Considérations techniques

Réseau

Par défaut, R n'intègre aucune interface réseau, et ne peut donc être utilisé à distance. Il existe une extension du nom de 'Rserve' permettant d'ajouter à R la possibilité de recevoir et traiter des requêtes TCP/IP, le rendant ainsi accessible à tous types de langages.

  • Avantages : R non nécessaire sur la machine cliente, délégation des calculs à une machine tierce, appli 100% portable

  • Inconvénient : Rserve à installer sur le serveur

JNI

L'utilisation de JNI implique la création d'une librairie dépendante du système. On perd donc un peu de la portabilité du Java.

  • Avantages : Installation basique de R

  • Inconvénients : Même machine, nécessite des paramètres de démarrage de l'application Java, l'application Java n'est plus 100% portable car création/compilation d'une librairie nécessaire.

Déroulement (protocole) des tests

Le but des tests est de faire ressortir le coût de chacune des solutions de manière à déterminer laquelle pourrait être la meilleure, mais surtout les conditions, s'il y en a, dans lesquelles telle ou telle solution est meilleure.

Deux types de tests ont été effectués.

  • Le premier consiste à envoyer de très petits calculs à R et ce beaucoup de fois de suite. On obtient donc une moyenne pour chacune des solutions ce qui permettra d'évaluer le coût de chaque méthode.

  • Le second se base sur la quantité de données à véhiculer. Il s'agit donc là de calculs plus long mais surtout générant un plus gros volume de données. Plusieurs mesures seront effectuées avec des tailles croissantes afin d'évaluer l'impact de l'augmentation volumétrique.

  • Le dernier test est surtout informatif et ne fera ressortir que le temps nécessaire à initialiser chacunes des solutions étudiées.

A noter que :

De façon à ne pas trop laisser libre cours aux optimisations des différentes plateformes, les calculs répétés sont volontairement changeants au sein d'un même test (ils restent néanmoins identiques entre les tests).

Résultats des tests

Test A - Calculs rapides

Les temps sont exprimés en milli-secondes.

_

Java

Net

JNI

R

Moyenne

_

241,06

233,56

230,83

Ecart-type

_

18,65

11,99

_

Test B - Volumes importants

Les temps sont exprimés en milli-secondes.

_

Java

Net

JNI

R

50

0,00

1,00

1,33

_

500

0,00

3,00

1,33

_

5000

1,00

13,00

3,33

_

50000

24,20

33,50

8,00

_

100000

60,20

118,00

79,00

_

200000

66,60

142,00

116,00

_

500000

89,00

330,00

189,00

_

1000000

94,00

620,00

282,00

_

Test C - Temps d'initialisation

Les temps sont exprimés en milli-secondes.

_

Java

Net

JNI

R

Init.

_

22,10

571,10

_

Exploitation des résultats

Les tests étant effectués, il faut maintenant les interpréter et en tirer des conclusions.

Test A - Calculs rapides

Le premier test avait pour but d'isoler le coût de chaque appel à R. La comparaison avec du Java pur n'avait ici aucun intêret dans la mesure ou le but est de calculer le temps d'appel à R.

Premièrement, l'opération est réalisée en R pur afin d'avoir un temps de base : 229.83ms. A partir de là, on peut estimer que le temps nécessaire à chaque technologie. Ainsi, un appel JNI est en moyenne d'un peu moins de 3ms (2.73ms), alors que par le réseau, il faut plus de 10ms (10,23ms), soit un ecart d'environ 7ms en faveur de JNI.

La différence est relativement faible, elle ne représente ici que 3%, mais répétée à grande échelle elle peut être pénalisante. Si par exemple 100.000 appels consécutifs sont nécessaires, la différence se monte à 700 secondes soit 11 minutes et 40 secondes ce qui peut être énorme pour des simulateurs par exemple.

Pour ce test, l'écart-type était mesuré afin d'estimer la répartition des valeurs. L'écart-type nous permet ici de constater que la répartition des valeurs avec JNI est plus proche de la moyenne que par le réseau. La solution réseau est donc plus sensible aux variations de temps.

Test B - Volumes importants

La deuxième série de tests veut mettre en valeur l'évolution des temps nécessaires avec l'augmentation du volume de données. Inversement au précédent test, c'est la solution R pur qui n'a ici pas de sens puisqu'on cherche à évaluer l'impact d'un passage de Java à R.

L'opération réalisée pour ce test est un simple produit scalaire entre deux vecteurs dont la taille va augmenter progressivement. Le graphique ci-dessous permet de voir instantannément la manière dont évoluent ces technologies :

Evolution des temps de réponse

L'étude de l'évolution des temps de réponse s'avère révélatrice.

A faibles volumes de données, les résultats des 3 solutions sont comparables.

Cependant, la solution réseau montre vite ses faiblesses. Dès l'utilisation de vecteurs d'une taille de 5.000 chiffres la comparaison devient ridicule tant les temps nécessaires pour transférer les vecteurs prend le pas sur le calcul. Pour le reste de ce test, le réseau restera toujours la solution la moins adaptée.

Du côté de JNI, la solution s'avère beaucoup plus pertinente. R sur JNI s'offre même le luxe de battre Java sur certaines tailles ce qui est très flatteur si on tient compte du fait que les résultats doivent être convertis de R à Java afin d'être exploitables. La solution se montre donc viable mais uniquement jusqu'à ce que la taille des vecteurs depasse les 100.000 chiffres. Les optimisations de Java prennent alors le dessus et affichent des temps bien inférieurs.

Enfin le dernier test sur des vecteurs d'une taille de 1.000.000 de chiffres indique des temps qui, même s'ils ne sont plus comparables à Java (94ms), restent honorables. La solution JNI nécessite 282ms pour effectuer le calcul et le renvoyer à Java, ce qui signifie qu'il est toujours possible d'effectuer 3 produits scalaires sur des vecteurs d'un million d'entrées en moins d'une seconde. Le temps nécessaire à la solution réseau (620ms) n'est pas non plus ridicule compte tenu de la taille des données à traiter. La solution réseau souffre simplement de temps de transfert trop longs qui sont le reflet habituel de l'utilisation des réseaux.

Test C - Temps d'initialisation

Le temps d'initialisation représente le temps nécessaire à la première utilisation de l'application avant de pouvoir accèder à R. Il peut parraitre annodin, mais en réalité il peut s'avérer primordial dans le choix de la solution à utiliser.

Si les tests précédents n'ont pas été en faveur de la solution réseau, ce test là montre que l'initialisation de JNI prend en moyenne 550ms de plus que l'initialisation du réseau. Cette différence peut s'avérer cruciale si les calculs à effectuer sont petits et peu répétés.

Notes

Certains faits n'apparaissent pas dans les chiffres précédemment cités mais peuvent également faire pencher la balance.

Par exemple, les différents tests ont fait ressortir que la solution réseau nécessite un "temps de chauffe". Un simple test répété 10 fois de suite peut le mettre en évidence. Chaque ligne suivante représente l'évolution d'un même test :

duration: 801ms

duration: 700ms

duration: 638ms

duration: 584ms

duration: 642ms

duration: 581ms

duration: 415ms

duration: 323ms

duration: 321ms

duration: 326ms

La différence entre le premier et le dernier test atteint quasiment la demi seconde soit plus de 150% du temps nécessaire au final pour l'opération.

Récapitulatif

Les tests effectués se sont montrés révélateurs.

Net

Cette solution s'est montrée moins efficace que les autres. La différence n'est pas pour autant dramatique puisque les temps restent tout à fait corrects.

Cette approche n'a pas que des inconvénients. En effet, la mise en place de la solution réseau est plus simple comparée à JNI et bénéficie également d'un temps d'initialisation beaucoup plus faible. A ne pas oublier aussi que le réseau offre la possibilité de mobiliser une seconde machine pour effectuer les calculs en R, ce qui permettra encore d'améliorer les performances. A noter cependant que les performances de la solution réseau s'améliorent avec le temps. Ceci signifie que la solution perd de son interêt seulement si quelques rares appels sont effectués.

JNI

La solution JNI a fait ses preuves sur les différents tests. Il s'est avéré que les temps d'appels étaient toujours inférieurs à la solution réseau.

Néanmoins, la solution JNI n'a pas que des avantages. La mise en place, par exemple, est plus complexe car elle nécessite une recompilation des sources JNI. On perd ainsi un peu de la portabilité de Java. D'autre part, la première initialisation de JNI s'avère particulièrement longue (une demi seconde) surtout si elle est comparée à la solution réseau.

Java

La comparaison n'a que peu d'interêt mais permet de faire ressortir la puissance de Java qui par le biais d'optimisations parvient à devancer R sur de nombreux calculs.

Il n'en reste pas moins que l'utilisation de R a des avantages. Les tests effectués ici ne se basaient que sur des calculs simples. En appelant des fonctions plus optimisées de R voire des librairies spécifiques, Java ne pourraient certainement plus rivaliser avec R.

Conclusion

Les deux principales solutions étudiées (JNI et réseau) ont toutes deux montrés des avantages et inconvénients et fait ressortir des cas dans lesquels elles sont préférables à l'autre.

Difficile donc de faire un choix. C'est pourquoi ce choix est laissé à l'utilisateur avec la librairie LutinJ2R qui propose une interface unifiée pour accéder aux fonctionnalités de R permettant ainsi de choisir à la volée quelle solution utiliser.