Conversion d’un entier vers une chaîne de caractères
Au cours d’une relecture de code, je tombe sur cette ligne :
int myInt = 1979; byte[] foo = ("" + myInt).getBytes();
L’objectif de ce code étant de récupérer un tableau de byte d’un entier en passant par une chaîne de caractères et ça fonctionne ! Mais cela est-il performant ?
Comme je suis curieux, j’ai regardé le bytecode et découvert que ce code créé un StringBuilder pour faire la conversion de l’entier vers la chaîne de caractères :
NEW java/lang/StringBuilder DUP INVOKESPECIAL java/lang/StringBuilder.()V ILOAD 1 INVOKEVIRTUAL java/lang/StringBuilder.append(I)Ljava/lang/StringBuilder; INVOKEVIRTUAL java/lang/StringBuilder.toString()Ljava/lang/String; INVOKEVIRTUAL java/lang/String.getBytes()[B ASTORE 2
En parcourant l’API, je découvre la méthode String.valueOf(int i) qui permet de convertir un entier en chaîne de caractères
int myInt = 1979; byte[] bar = String.valueOf(myInt).getBytes();
Cela semble mieux en analysant le bytecode qui ne créé pas de nouvel objet mais utilise une méthode statique :
ILOAD 1 INVOKESTATIC java/lang/String.valueOf(I)Ljava/lang/String; INVOKEVIRTUAL java/lang/String.getBytes()[B ASTORE 2
Cela semble plus performant mais comme je suis très curieux, j’ai regardé l’implémentation de String.valueOf(int i) qui en fait, se contente d’un appel à Integer.toString(int) :
int myInt = 1979; byte[] bar = Integer.toString(myInt).getBytes();
Le bytecode ressemble beaucoup au précédent mais évite un appel supplémentaire.
ILOAD 1 INVOKESTATIC java/lang/Integer.toString(I)Ljava/lang/String; INVOKEVIRTUAL java/lang/String.getBytes()[B ASTORE 2
Voyons maintenant si ce code améliore les performances. Le test de performance est fait sur la [CONFIG_ARM926EJ-S] avec JamVM 1.5.4 / GnuClasspath 0.98 / Java 1.5 :
| Nombre d'appel | StringBuilder | String.valueOf(int i) | Integer.toString(int) |
|---|---|---|---|
| 1 | 549ms | 9ms | 7ms |
| 5 | 594ms | 71ms | 15ms |
| 100 | 865ms | 342ms | 238ms |
En conclusion, le fait d’utiliser String.valueOf(int i) ou mieux, Integer.toString(int i), permet de gagner en performance d’exécution mais aussi en mémoire car cela évite de crée de nouveau objet intermédiaire.
Plus généralement, il est toujours préférable d’utiliser les méthodes statiques des classes de bases tel que String, Integer qui seront plus performantes que les méthodes des instances.

envoi en cours...
Les grands esprits se rencontrent, on dirait… Mes deux derniers articles traitaient justement du sujet :
- http://thecodersbreakfast.net/index.php?post/2011/11/07/Au-coeur-du-JDK-conversion-des-nombres
- http://thecodersbreakfast.net/index.php?post/2011/11/15/Au-coeur-du-JDK-performance-des-conversions
Sinon, est-ce que tu pourrais fournir le code source de ton benchmark, pour qu’on puisse examiner le protocole de test, et le faire tourner chez nous ?
Merci Olivier pour tes liens, effectivement nous étudions les mêmes sujets. De ton côté, tu es plus complet et moi je suis plus prêt de l’embarqué. Nous sommes donc complémentaire et arrivons au même résultat, heureusement
Concernant mes méthodes de tests, j’utilise tout simplement des System.currentTimeMillis(); mais j’aimerai écrire un petit framework léger pour mieux mesurer les indicateurs. Peut être que tu pourrais me montrer ton protocole de test pour me donner quelques idées…
Je vais essayer de rajouter le code source à mes articles, c’est une bonne idée !
Merci
Le code du benchmark est en pièce jointe de mon billet.
Merci Olivier, si cela te dérange pas, je vais écrire un petit framework pour tester les performances (CPU et mémoire) sur cibles qui s’inspire en partie de ton code.