calcul mathematique simple, optimisation Java - Java - Programmation
Marsh Posté le 24-09-2009 à 20:57:25
Code :
|
tu es sûr de vouloir calculer cor[i][j] 4096 fois (et d'écraser la valeur précédente à chaque fois) ?
A mon avis il manque un "+"
Marsh Posté le 24-09-2009 à 20:58:38
il devrait y avoir un "+=" au lieu du "=", exact
mais a la limite ca ne change rien, vu que l'erreur est dans les deux, non?
Marsh Posté le 24-09-2009 à 20:59:29
par contre ça risque de démultiplier le temps de calcul
si les compilo sont pas cons ils ont supprimé cette boucle
Marsh Posté le 24-09-2009 à 20:59:58
ce qui importe ici c'est que les deux fassent la meme chose, pas l'exactitude du calcul
Marsh Posté le 24-09-2009 à 21:04:19
bon on pouvait s'y attendre, les deux executions sont plus lentes apres cette petite modif (merci sligor de l'avoir mentionnee, effectivement on aurait pu imaginer que le compilo avait optimise ca en prenant juste la 4096e valeur... mais en fait non)
nouveaux temps d'execution:
# gcc -lm cr.c -o cr && time ./cr |
la question demeure
Marsh Posté le 24-09-2009 à 21:08:05
il sont cons ces compilateurs
Sinon: tu as des tableau énormes (~64Mo) donc tu exploses la mémoire cache et le CPU se tourne les pouces à attendre les datas à chaque fois.
Tu effectues des calculs compliqués sqrt float & div float: le cpu se tourne les pouces en attendant la fpu. La performance de la machine impacte beaucoup plus le résultat que les optimisations de compilation
Marsh Posté le 24-09-2009 à 21:09:26
un autre truc, dans la meme portion de code, je calcule k fois 4096*Math.sqrt(var[i]*var[j]) alors qu'une suffirait (en pratique je fais ca souvent pour pas faire exploser des limites hautes pour les entiers, mais ici ca a peu de sens)
me demande si gcc est assez intelligent pour optimiser ca, je teste
Marsh Posté le 24-09-2009 à 21:10:29
>sligor
ok ca a du sens
maintenant, pourquoi cette difference entre l'execution C et Java?
Marsh Posté le 24-09-2009 à 21:12:57
au passage: -ffast-math peut être très utile: testé et approuvé
Marsh Posté le 24-09-2009 à 21:13:55
deja teste, ca ralentit mon execution C de 5% (va savoir pourquoi)
Marsh Posté le 24-09-2009 à 21:16:46
tu es sûr ? chez moi je gagne presque un rapport 10 juste en ajoutant cette option
Quelle version de gcc au passage ?
gcc -lm -O3 -mfpmath=sse -msse2 cr.c -o cr -ffast-math
Marsh Posté le 24-09-2009 à 21:20:39
apres modif du diviseur dans corr[i][j] (des deux cotes bien sur)
et test --fast-math (c'etait en fait un autre flag qui me donnait -5%, j'ai parle trop vite)
|
je comprends plus rien
Marsh Posté le 24-09-2009 à 21:21:34
ah ouais
|
Marsh Posté le 24-09-2009 à 21:24:53
en fait -O3 avec -ffast-math permet à GCC de vectoriser le code
tu peux utiliser "-ftree-vectorizer-verbose=5" pour voir ce qu'il arrive à vectoriser. Mais de toute façon la boucle qui prend le plus de temps et qui est donc la plus importante est la dernière
Marsh Posté le 24-09-2009 à 21:27:16
en augmentant la taille des tableaux pour avoir qqch de plus significatif:
|
c'est quoi tout ce temps system en Java
et pourquoi 183.3%? le temps system serait il execute en // ?
Marsh Posté le 24-09-2009 à 21:32:51
questions restantes:
- pourquoi tant de temps CPU en plus en Java par rapport a C?
- qu'est ce que ce temps systeme?
(optionnel)
- pourquoi 64io en Java et 0 en C? pourquoi 1pf en Java et 0 en C?
Marsh Posté le 24-09-2009 à 21:38:05
A vue de nez ca ressemble à un optimisation de java (fais donc un htop pendant l'execution, tu vas comprendre).
D'ailleurs si tu regardes bien le rapport de time :
C : 99.9 % (1 CPU)
Java : 194.5% (2 CPUs)
Après, quelle est cette optimisation...
Marsh Posté le 24-09-2009 à 21:45:02
entre parentheses, avec -mtune=native C est encore plus rapide (1s de moins que sans)
|
>e_esprit
en theorie ce calcul est parfaitement parallelisable, c'est a dire que si j'ai 8 cpu je vais quasi 8 fois plus vite
maintenant, java est 80% plus lent mais utilise 2 CPU donc bat C sur la ligne d'arrivee... mais que fait il exactement, et pourquoi?
Marsh Posté le 24-09-2009 à 22:49:44
Ayayaye, encore quelqu'un qui veut comparer des pommes et des oranges...
Bon alors c'est simple: quand tu lances programme Java, tu lances la JVM (un énorme programme très complexe) qui à son tour lance ton programme. Tu ne peux pas comparer le temps qu'il faut pour démarrer une JVM avec le temps qu'il faut pour démarrer un programme C déjà compilé qui est nul ou presque. La JVM est lourde à charger, c'est connu.
Si tu veux vraiment chronométrer un temps d'exécution, tu dois mesurer le temps d'exécution de l'application en récupérant une valeur sur l'horloge système juste avant et juste après le calcul (par exemple avec System.currentTimeMillis() même si ce n'est pas le plus précis), sinon tu vas mesurer également le temps qu'il faut pour démarrer et arrêter la JVM!
De plus, la JVM commence par interpréter puis compile nativement les parties du code les plus fréquemment utilisées au fil du temps (compilateur JIT intelligent), ce qui veut dire que la première exécution du calcul sera plus lente que la dernière si tu en fais plusieurs. Raison pour laquelle quand on fait des benchmarks Java, il est plus "fair play" de ne pas chronométrer la première exécution du code ni même la deuxième, mais plutôt la 3e ou 4e exécution (sans arrêter la JVM entre chaque exécution bien entendu!).
Tu devrais alors mesurer des performances quasi semblables avec sans doute un avantage pour le code C, en particulier parce que les calculs de type racine carrée, sinus et cosinus sont plus lents en Java qu'en C.
Enfin sache qu'il existe un JVM client et une JVM serveur (dont la disponibilité varie d'une plate-forme à l'autre) qui peuvent offrir des performances différentes.
Marsh Posté le 24-09-2009 à 22:53:10
faut pas délirer, sur 23s le temps de lancement de la JVM est très négligeable. De même pour le JIT, ici on execute une boucle plusieurs millions de fois
Marsh Posté le 24-09-2009 à 22:58:43
En tous cas la JVM explique les IO et le reste des différences avec le code C.
Effectivement si on exécute une racine carrée plusieurs millions de fois, alors que cette opération est connue pour être beaucoup plus lente en java qu'en C, ce n'est pas étonnant que ce soit très lent
Mais bon je pense que comparer du Java et du C pour savoir lequel est le "meilleur" n'a aucun sens. Comparer plusieurs JVM ou implémentations Java serait plus intéressant.
Tu pourrais aussi essayer le compilateur java natif GCJ versus GCC.
Marsh Posté le 24-09-2009 à 23:20:48
>cbeyls
merci pour ces explications
le but ici n'etait pas de dire "qui ou qui est le plus rapide" (ca je crois qu'on le sait) mais "qui fait quoi et comment" pour expliquer les differences
et ton post m'eclaire sur certains points
Marsh Posté le 24-09-2009 à 23:36:35
Content d'avoir éclairé ta lanterne!
Petite remarque quand même: le C n'est pas toujours le plus rapide contrairement à ce qu'on croit. En particulier, Java peut être plus efficace que du code C pour l'allocation de mémoire, la gestion des Exceptions et autres. Et avec chaque nouvelle version de Java, les performances sont améliorées.
Avant de me faire démolir par les pro-C, je tiens à préciser que j'aime et utilise de nombreux langages dont le Java et le C
Marsh Posté le 24-09-2009 à 23:38:13
Quand les exceptions impactent les performances c'est qu'elle sont mal utilisées (valable pour n'importe quel langage)
Marsh Posté le 24-09-2009 à 23:39:26
Joelf ! viens nous apprendre à parcourir une matrice !
(sinon je pow(x, 2) faut voir à faire soit même le * et zieuter l'asm, des fois c'est mieux).
Marsh Posté le 24-09-2009 à 23:43:04
question subsidiaire:
qu'est ce que la JVM fait avec mon kernel pendant 30% du temps cpu?
Marsh Posté le 24-09-2009 à 23:44:59
cbeyls a écrit : |
Le type de JVM et de compilo JIT va avoir une influence non négligeable en terme d'exécution de code, l'architecture utilisée joue pas mal aussi ; à ce niveau je pense plus particulièrement aux implémentations Zero/Shark vs Cacao.
Aujourd'hui, en matière de JDK, l'implémentation open source OpenJDK est aussi performante (si ce n'est plus performante) que celle de chez Sun.
Marsh Posté le 24-09-2009 à 23:51:24
mes tests ci-dessus sont OpenJDK+Hotspot
je vais regarder Zero/Shark et Cacao
Marsh Posté le 24-09-2009 à 20:51:31
ci dessous le code et le temps d'execution d'un petit programme tout simple, en C et en Java
$ cat cr.c
#include "math.h"
#define SIZEA 1024
#define SIZEB 4096
static double mat[SIZEA][SIZEB];
static double var[SIZEA];
static double cor[SIZEA][SIZEA];
int main(int argc, char** argv) {
int i,j,k,l;
for (i=0;i<SIZEA;i++) {
for (j=0;j<SIZEB;j++) {
mat[i][j]=pow(-1,i)*i/(j+1.0);
}
}
for (i=0;i<SIZEA;i++) {
double mean;
mean = 0;
for (j=0;j<SIZEB;j++) {
mean+=mat[i][j];
}
mean/=SIZEB;
double variance = 0;
for (j=0;j<SIZEB;j++) {
variance+=pow((mat[i][j]-mean),2);
}
variance/=SIZEB;
var[i]=variance;
}
// calculate correlation
for (i=0;i<SIZEA;i++) {
for (j=0;j<SIZEA;j++) {
cor[i][j]=0;
for(k=0;k<SIZEB;k++) {
cor[i][j]=mat[i][k]*mat[j][k]/(SIZEB*sqrt(var[i]*var[j]));
}
}
}
return 0;
}
$ cat cr.java
class Cr {
public static void main(String [] args) {
double[][] mat = new double[1024][4096];
double[] var = new double[1024];
double[][] cor = new double[1024][1024];
int i,j,k,l;
for (i=0;i<1024;i++) {
for (j=0;j<4096;j++) {
mat[i][j]=Math.pow(-1,i)*i/(j+1.0);
}
}
for (i=0;i<1024;i++) {
double mean;
mean = 0;
for (j=0;j<4096;j++) {
mean+=mat[i][j];
}
mean/=4096;
double variance=0;
for (j=0;j<4096;j++) {
variance+=Math.pow((mat[i][j]-mean),2);
}
variance/=4096;
var[i]=variance;
}
// calculate correlation
for (i=0;i<1024;i++) {
for (j=0;j<1024;j++) {
cor[i][j]=0;
for(k=0;k<4096;k++) {
cor[i][j]=mat[i][k]*mat[j][k]/(4096*Math.sqrt(var[i]*var[j]));
}
}
}
}
};
$ gcc -lm cr.c -o cr
$ gcc --version
gcc (Debian 4.3.4-2) 4.3.4
Copyright (C) 2008 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
$ javac cr.java
$ java -version
java version "1.6.0_0"
OpenJDK Runtime Environment (IcedTea6 1.5) (6b16-4)
OpenJDK 64-Bit Server VM (build 14.0-b15, mixed mode)
$ time ./cr
87.9u 0.0s 1:27.92 99.9% 0+0k 0+0io 0pf+0w
$ time java Cr
117.5u 46.8s 1:24.47 194.6% 0+0k 0+112io 1pf+0w
Constatations:
- le temps CPU user+system consomme est plus faible pour le binaire C que pour le semi-binaire Java (Java 80% plus lent)
- le temps d’execution est plus lent de 3s sur 1min30s environ avec avantage pour Java
- C a tout son temps en “user” alors que Java a une repartition 70-30 pour user et system
Questions:
1) Pourquoi le temps CPU C est il bien plus faible que Java? Je m’attendais a ce que Java soit un peu plus lent, le temps de convertir le .class en instructions machines, mais pas dans de telles proportions?
2) Pourquoi C consomme t’il du temps user quasi uniquement, et Java du temps user et systeme?
3) Pourquoi le temps d’execution reel de Java est il plus court malgre le temps CPU quasi 2x plus long? Mon programme n’est pas multi-thread, donc ca ne devrait pas arriver?
Merci
--ztg