Divers
Optimisation
Cette page résume et commente quelques mesures des performances de J2SE 6 effectuées lors du
développement de jChecs.
Même si la véritable optimisation reste de choisir le bon algorithme et si jChecs n'a pas
pour but d'être optimal, ceci peut fournir des pistes pour optimiser le codage final d'un algorithme
en fonction de ce nouvel environnement...
Toutes les mesures ont été prises avec la JVM 6 (beta 104) de Sun pour Linux, sans
option particulière.
Une mesure est prise en référence (en gras) pour chaque série et les autres résultats
sont donnés comparativement : le plus grand nombre est le meilleur.
Boucles
for (en avant) |
100% |
Les boucles inversées (décrémentées) n'ont plus vraiment d'intérêt
avec cette JVM.
Un « while » en avant (incrémenté) reste moins
performant que le « for » équivalent.
|
for (en arrière) |
100% |
while (en avant) |
75% |
while (en arrière) |
100% |
Variables
Statique |
100% |
Les temps d'accès aux différents types de variables deviennent
comparables, mais les variables statiques restent les plus lentes.
|
Instance |
128% |
Locale |
131% |
Autoboxing
int / new Integer(int).intValue() |
100% |
- Les constructeurs, supposés archaïques, restent plus rapides que les nouvelles méthodes
de fabriques des types simples.
- L'autoboxing a un coût négligeable sur les performances.
- Les types primitifs sont nettement plus rapides...
|
int / Integer.valueOf(int).intValue() |
95% |
int / Integer (Autoboxing) |
94% |
int / int |
5 688% |
Allocation de tableaux
Tableaux d'objets |
- L'allocation de tableaux est quasiment aussi rapide pour les objets que pour les types
primitifs.
- A nombres d'éléments égaux, il est plus efficace d'allouer un tableau à une dimension
qu'un tableau à n dimensions.
Le nombre d'éléments d'un tableau a un impact très fort sur les
performances de l'allocation : ne pas allouer plus d'éléments que nécessaire.
|
Integer[64][64] |
100% |
Integer[64*64] |
145% |
Integer[32*64] |
289% |
Integer[0] |
121 132% |
Tableaux de primitives |
int[64][64] |
104% |
int[64*64] |
144% |
int[32*64] |
291% |
int[0] |
121 257% |
Copie de tableaux
Tableaux d'objets |
« System.arraycopy() » demeure la méthode la plus efficace pour
copier des tableaux à une dimension, mais de très peu devant « Arrays.copyOf() ».
- Ne pas utiliser de boucle pour recopier des tableaux, surtout d'objets.
- Les boucles inversées (décrémentées) sont à éviter lors d'accès consécutifs aux
éléments d'un (grand) tableau.
|
System.arraycopy(Integer) |
100% |
Arrays.copyOf(Integer) |
99% |
for (Integer, en avant) |
49% |
for (Integer, en arrière) |
37% |
Tableaux de primitives |
System.arraycopy(int) |
100% |
Arrays.copyOf(int) |
100% |
for (int, en avant) |
95% |
for (int, en arrière) |
91% |
Parcours de listes d'éléments
ArrayList d'objets |
- Le moyen le plus efficace pour parcourir une « ArrayList » est une boucle
réalisant des accès indexés (des « get() »).
- Le moyen le plus efficace pour parcourir un « Vector » est d'utiliser son
énumérateur.
- La forme étendue du « for », introduite avec J2SE 5.0, est optimale pour
parcourir les éléments d'un tableau.
- Les tableaux demeurent plus rapides que les collections, et les primitives plus rapides
que les objets.
- Parcourir à l'envers une liste d'éléments a toujours un impact négatif sur les
performances.
- Un « Vector », synchronisé et donc théoriquement plus lent, se montre pourtant
plus efficace qu'une « ArrayList » dans ce cas.
|
for sur Enumeration (Java 1.0) |
100% |
while sur Enumeration (Java 1.0) |
100% |
for sur Iterator (Java 1.2) |
132% |
while sur Iterator (Java 1.2) |
133% |
for étendu (J2SE 5.0) |
121% |
Accès indexés (get, en avant) |
205% |
Accès indexés (get, en arrière) |
163% |
Vector d'objets |
|
for sur Enumeration (Java 1.0) |
218% |
while sur Enumeration (Java 1.0) |
218% |
for sur Iterator (Java 1.2) |
161% |
while sur Iterator (Java 1.2) |
161% |
for étendu (J2SE 5.0) |
160% |
Accès indexés (get, en avant) |
208% |
Accès indexés (get, en arrière) |
167% |
Tableau d'objets |
|
for étendu (J2SE 5.0) |
403% |
Accès indexés (get, en avant) |
391% |
Accès indexés (get, en arrière) |
313% |
Tableau de primitives |
|
for étendu (J2SE 5.0) |
1 325% |
Accès indexés (get, en avant) |
1 211% |
Accès indexés (get, en arrière) |
960% |
Concaténation de chaînes
Chaînes variables |
Utiliser un « StringBuilder » est le moyen le plus performant
pour concaténer des chaînes calculées à l'exécution.
La concaténation de chaînes constantes est plus performante avec des
« String », car pré-calculée à la compilation.
|
String |
100% |
StringBuffer |
109% |
StringBuilder |
111% |
Chaînes constantes |
String |
381% |
StringBuffer |
220% |
StringBuilder |
223% |
Types génériques
Lecture |
Une vérification de type par rapport à la variable cible est introduite à
l'éxécution, et donc réduit les performances, si la cible n'est pas déclarée avec le même type que
la source ou comme « Object ».
|
Integer = List<Integer> |
100% |
Integer = List<Object> |
97% |
Integer = List<? super Number> |
95% |
Object = List<Integer> |
112% |
Object = List<Object> |
119% |
Object = List<? super Number> |
113% |
Ecriture |
List<Integer> = Integer |
93% |
List<Object> = Integer |
94% |
List<? super Number> = Integer |
94% |
|