Parce que je constate qu'elles varient d'une exécution à
l'autre. Et que du fait que mon compteur de boucle est surn un
entier 32 bits, je ne peux pas augmenter la durée de façon
illimitée. (Normallement, je considère quelque chose comme cinq
minutes pour chaque mésure comme un minimum. Mais ici, j'en suis
loin.)
Je n'ai jamais vu un facteur 5. J'ai vu -10%/+50% au maximum.
Et c'est coherent avec un appel indirect de fonction via un
pointeur.
On est déjà à plus de quatre ci-dessus, sur un Sparc plutôt
ancien. Des personnes de chez HP (à l'époque) m'ont assuré que
sur l'architecture PA Risc, c'était pire.
Le problème, c'est que c'est un branchement indirect. Ce qui pose
des problèmes pour la prédiction des branchements. Dans le cas du
HP, d'après ce que j'ai compris, il en résulte un flush du pipeline
à chaque coup.
Parce que je constate qu'elles varient d'une exécution à
l'autre. Et que du fait que mon compteur de boucle est surn un
entier 32 bits, je ne peux pas augmenter la durée de façon
illimitée. (Normallement, je considère quelque chose comme cinq
minutes pour chaque mésure comme un minimum. Mais ici, j'en suis
loin.)
Je n'ai jamais vu un facteur 5. J'ai vu -10%/+50% au maximum.
Et c'est coherent avec un appel indirect de fonction via un
pointeur.
On est déjà à plus de quatre ci-dessus, sur un Sparc plutôt
ancien. Des personnes de chez HP (à l'époque) m'ont assuré que
sur l'architecture PA Risc, c'était pire.
Le problème, c'est que c'est un branchement indirect. Ce qui pose
des problèmes pour la prédiction des branchements. Dans le cas du
HP, d'après ce que j'ai compris, il en résulte un flush du pipeline
à chaque coup.
Parce que je constate qu'elles varient d'une exécution à
l'autre. Et que du fait que mon compteur de boucle est surn un
entier 32 bits, je ne peux pas augmenter la durée de façon
illimitée. (Normallement, je considère quelque chose comme cinq
minutes pour chaque mésure comme un minimum. Mais ici, j'en suis
loin.)
Je n'ai jamais vu un facteur 5. J'ai vu -10%/+50% au maximum.
Et c'est coherent avec un appel indirect de fonction via un
pointeur.
On est déjà à plus de quatre ci-dessus, sur un Sparc plutôt
ancien. Des personnes de chez HP (à l'époque) m'ont assuré que
sur l'architecture PA Risc, c'était pire.
Le problème, c'est que c'est un branchement indirect. Ce qui pose
des problèmes pour la prédiction des branchements. Dans le cas du
HP, d'après ce que j'ai compris, il en résulte un flush du pipeline
à chaque coup.
Jean-Marc Bourguet wrote:Mon idee est que maintenant essayer de separer les choses de leur
contexte n'a plus guere de sens parce que le cout depend fortement du
contexte; ca ne m'etonnerait pas du tout qu'on se retrouve meme
parfois avec des couts negatifs.
Dans le cas present, la premiere chose a noter, c'est que quand on
compile pour des objets partages (code PIC), on se retrouve souvent
(au moins sous Unix, je suppose pour Windows aussi) a ce que des
appels "directs" soient aussi en fait des appels indirects (je suppose
que B.S et toi savez ca et en avez tenu compte).
Oui, mais dans ce cas, on a un appel indirect. On peut donc tester
la rapidite d'un appel indirect hors PIC et en deduire le coup d'un
appel direct avec PIC.
Deuxieme chose, ensuite, quels sont les facteurs qui comptent dans le
temps d'execution d'un appel?
1/ est-ce que la page est en memoire
independant de direct-indirect2/ est-ce que l'entree de la page est dans le TLB
independant de direct-indirect3/ est-ce que la fonction est dans le cache
independant de direct-indirect4/ est-ce que l'appel est predictible (est-ce qu'il est pris ou non,
est-ce que sa destination est predictible ou non).
la d'accord, et c'est justement en partie ce que l'on mesure.
A mon avis ces quatres facteurs vont en pratique dominer la
difference entre un appel direct et un appel indirect et ces
quatres facteurs sont dependants du contexte.
Je pense que les trois premiers ne sont pas representatif du coup
d'un appel direct - indirect.
Mon point de vue est de dire que les appels virtuels sont en general
aussi rapides que les appels direct. L'objectif est d'eviter que des
programmeurs partent dans des delires algorithmiques pour ne pas
utiliser les fonctions virtuelles pour cause d'efficacite.
Jean-Marc Bourguet wrote:
Mon idee est que maintenant essayer de separer les choses de leur
contexte n'a plus guere de sens parce que le cout depend fortement du
contexte; ca ne m'etonnerait pas du tout qu'on se retrouve meme
parfois avec des couts negatifs.
Dans le cas present, la premiere chose a noter, c'est que quand on
compile pour des objets partages (code PIC), on se retrouve souvent
(au moins sous Unix, je suppose pour Windows aussi) a ce que des
appels "directs" soient aussi en fait des appels indirects (je suppose
que B.S et toi savez ca et en avez tenu compte).
Oui, mais dans ce cas, on a un appel indirect. On peut donc tester
la rapidite d'un appel indirect hors PIC et en deduire le coup d'un
appel direct avec PIC.
Deuxieme chose, ensuite, quels sont les facteurs qui comptent dans le
temps d'execution d'un appel?
1/ est-ce que la page est en memoire
independant de direct-indirect
2/ est-ce que l'entree de la page est dans le TLB
independant de direct-indirect
3/ est-ce que la fonction est dans le cache
independant de direct-indirect
4/ est-ce que l'appel est predictible (est-ce qu'il est pris ou non,
est-ce que sa destination est predictible ou non).
la d'accord, et c'est justement en partie ce que l'on mesure.
A mon avis ces quatres facteurs vont en pratique dominer la
difference entre un appel direct et un appel indirect et ces
quatres facteurs sont dependants du contexte.
Je pense que les trois premiers ne sont pas representatif du coup
d'un appel direct - indirect.
Mon point de vue est de dire que les appels virtuels sont en general
aussi rapides que les appels direct. L'objectif est d'eviter que des
programmeurs partent dans des delires algorithmiques pour ne pas
utiliser les fonctions virtuelles pour cause d'efficacite.
Jean-Marc Bourguet wrote:Mon idee est que maintenant essayer de separer les choses de leur
contexte n'a plus guere de sens parce que le cout depend fortement du
contexte; ca ne m'etonnerait pas du tout qu'on se retrouve meme
parfois avec des couts negatifs.
Dans le cas present, la premiere chose a noter, c'est que quand on
compile pour des objets partages (code PIC), on se retrouve souvent
(au moins sous Unix, je suppose pour Windows aussi) a ce que des
appels "directs" soient aussi en fait des appels indirects (je suppose
que B.S et toi savez ca et en avez tenu compte).
Oui, mais dans ce cas, on a un appel indirect. On peut donc tester
la rapidite d'un appel indirect hors PIC et en deduire le coup d'un
appel direct avec PIC.
Deuxieme chose, ensuite, quels sont les facteurs qui comptent dans le
temps d'execution d'un appel?
1/ est-ce que la page est en memoire
independant de direct-indirect2/ est-ce que l'entree de la page est dans le TLB
independant de direct-indirect3/ est-ce que la fonction est dans le cache
independant de direct-indirect4/ est-ce que l'appel est predictible (est-ce qu'il est pris ou non,
est-ce que sa destination est predictible ou non).
la d'accord, et c'est justement en partie ce que l'on mesure.
A mon avis ces quatres facteurs vont en pratique dominer la
difference entre un appel direct et un appel indirect et ces
quatres facteurs sont dependants du contexte.
Je pense que les trois premiers ne sont pas representatif du coup
d'un appel direct - indirect.
Mon point de vue est de dire que les appels virtuels sont en general
aussi rapides que les appels direct. L'objectif est d'eviter que des
programmeurs partent dans des delires algorithmiques pour ne pas
utiliser les fonctions virtuelles pour cause d'efficacite.
wrote:
Les temps absolus sont peut parlant (depends des
architectures et des compilateurs). Je prefere de loin un
ratio virtuel/non-virtuel.
Mais ça aussi dépend de l'architectures et des compilateurs.
Et
Oui mais ce que l'on cherche, ce n'est pas de savoir combien
de temps met une operation, mais son efficacite par rapport a
une autre operation. C'est cela qui conditionne le
comportement des programmeurs.
beaucoup. Disons que sur une machine généraliste moderne, je
m'attendrais à un rapport entre 1:2 et 1:10. Sur des
machines plus primitives (embarquées ou anciennes), j'ai
déjà vu nettement moins de 1:2.
Jamais vu de tel rapport.
Heureusement parce que OOC n'a que des methodes virtuelles. Je
n'aurais jamais fait ca si je n'etais pas convaincu que
virtuel = direct +/- 10% (note que j'ai mesure des appels
virtuel plus rapide que des appels directs).
Je dirais que la différence entre virtuel et non-virtuel
(facteur 5), c'est à peu près ce à laquelle je m'attendrais
sur
Je n'ai jamais vu un facteur 5. J'ai vu -10%/+50% au
maximum. Et c'est coherent avec un appel indirect de
fonction via un pointeur.
On est déjà à plus de quatre ci-dessus, sur un Sparc plutôt
ancien. Des personnes de chez HP (à l'époque) m'ont assuré
que sur l'architecture PA Risc, c'était pire.
Je continue de penser que tu mesures inline vs virtual (ou
direct).
Le problème, c'est que c'est un branchement indirect. Ce qui
pose des problèmes pour la prédiction des branchements. Dans
le cas du HP, d'après ce que j'ai compris, il en résulte un
flush du pipeline à chaque coup. > > Personnellement je ne
crois pas. Cela ecroulerait les performances de > facon
mesurable et en ferait une architecture tres inefficace
(mais je > n'ai pas HP sous la main...). La plupart des
langages+projets > aujourd'hui utilisent des appels
indirects (interfaces). > Note que je ne mésure que le coup
de l'appel d'une fonction triviale (qui renvoie un int
constante) : « i = f() ; ». Il y a donc non seulement
l'appel de la fonction proprement dit, mais aussi chargement
du pointeur this, chargement et affectation de la valeur de
retour, et le retour lui-même. La différence dans le temps
de l'appel lui-même est donc encore plus grande.
Quand je fais ce type de tests, je fais avec:
- 0 arg + 0 ret
- 1 arg + 0 ret
- 0 arg + 1 ret
- 1 arg + 1 ret
- 2 args
Ca me donne une idee des performances plus realiste.
De l'autre côté, il faut le comparer à ce qu'on fait dans la
fonction. Dès qu'il y a une multiplication ou une division
entière, avec des termes non constantes, le temps de l'appel
devient négligeable. Sur ma machine... on ne peut pas faire
de généralités.
On ne parle que du cout de l'appel, le reste est speculatif.
C'est l'argument des utilisateurs d'Objective-C qui clame que
le message dispatch (bien que tres lent, 800% pour gcc) est
suffisament rapide en raison du code de la fonction. Et quand
tu demandes comment faire une classe Complex efficace (je te
rappelle que les data membres ne sont accessible que par des
methodes), ils te repondent que Objective-C n'est pas fait
pour ca...
Au passage j'ai une implementation de message dispatch en C
(type Objective-C) qui a un overhead de 40% a 80% par
rapport a un appel direct.
Sur quelle machine ? Mésuré comment ?
Linux IA32, gcc 3.3.5
clock_t, duree > 10 sec, elimination des couts "parasites", ratio.
C'est une implemantation qui ne supporte pas le message
forwarding avec autre chose que [jusqu'a] deux objects (idem
Stepstone objc). Cette remarque est importante car elle
"cristalise" le choix de l'implementation du dispatcher:
- soit le message dispatch recherche le pointeur de fonction a
partir du selecteur et le renvoi a l'appelant qui l'invoque
(POC, mon dispatcher version rapide). Cette techique a
l'avantage de permettre d'inliner le code de recherche dans le
cache qui avec mes tests arrive a 99,7% de hits sur 1000000 de
messages generes.
- soit tout est transfere (argument inclus), au dispatcher qui
forward le tout a la methode une fois trouvee (GCC, mon
dispatcher version lente). Cette solution permet de forwarder
n'importe quel message, au prix systematique d'un double appel
de fonction, donc performance > 200% (240-300% dans mon cas).
Ensuite tu as le choix de l'implementation du cache: GCC
utilise un sparse array a 2 (ou 3 niveaux configurable a la
compilation), le mien utilise une hash table.Et c'est forcement plus lent qu'un method lookup. Alors j'ai
du mal a imaginer un facteur 5 pour les methodes virtuelles.
C'est cependant le cas, au moins sur les machines modernes.
Est-ce que les appels directs sont dans une TU sparee avec une
compilation separee?
Parce que un facteur 5, c'est ce que je mesure entre inline et
direct (ou virtual).
Tu ne comparais pas avec de l'inline par harsard?
D'ou ma question ;-)Est-ce que tu as au moins lu ce que j'ai posté ? J'ai fait
Oui mais je n'ai ni le code, ni la methode de compilation.
plusieurs mésures, dans des conditions différentes. J'ai
bien l'impression que g++ a inliné les fonctions, parce
qu'il n'y a
C'est ce que je pense.pas de différences entre les temps pour les fonctions
inlinées, et les temps pour les fonctions non-virtuelles. Et
que ces temps
Bingo...pour les fonctions inlinées ressemblent à peu près à celles
de Sun CC. Dans le cas de Sun CC, en revanche, il y a bien
une différence.
kanze@gabi-soft.fr wrote:
Les temps absolus sont peut parlant (depends des
architectures et des compilateurs). Je prefere de loin un
ratio virtuel/non-virtuel.
Mais ça aussi dépend de l'architectures et des compilateurs.
Et
Oui mais ce que l'on cherche, ce n'est pas de savoir combien
de temps met une operation, mais son efficacite par rapport a
une autre operation. C'est cela qui conditionne le
comportement des programmeurs.
beaucoup. Disons que sur une machine généraliste moderne, je
m'attendrais à un rapport entre 1:2 et 1:10. Sur des
machines plus primitives (embarquées ou anciennes), j'ai
déjà vu nettement moins de 1:2.
Jamais vu de tel rapport.
Heureusement parce que OOC n'a que des methodes virtuelles. Je
n'aurais jamais fait ca si je n'etais pas convaincu que
virtuel = direct +/- 10% (note que j'ai mesure des appels
virtuel plus rapide que des appels directs).
Je dirais que la différence entre virtuel et non-virtuel
(facteur 5), c'est à peu près ce à laquelle je m'attendrais
sur
Je n'ai jamais vu un facteur 5. J'ai vu -10%/+50% au
maximum. Et c'est coherent avec un appel indirect de
fonction via un pointeur.
On est déjà à plus de quatre ci-dessus, sur un Sparc plutôt
ancien. Des personnes de chez HP (à l'époque) m'ont assuré
que sur l'architecture PA Risc, c'était pire.
Je continue de penser que tu mesures inline vs virtual (ou
direct).
Le problème, c'est que c'est un branchement indirect. Ce qui
pose des problèmes pour la prédiction des branchements. Dans
le cas du HP, d'après ce que j'ai compris, il en résulte un
flush du pipeline à chaque coup. > > Personnellement je ne
crois pas. Cela ecroulerait les performances de > facon
mesurable et en ferait une architecture tres inefficace
(mais je > n'ai pas HP sous la main...). La plupart des
langages+projets > aujourd'hui utilisent des appels
indirects (interfaces). > Note que je ne mésure que le coup
de l'appel d'une fonction triviale (qui renvoie un int
constante) : « i = f() ; ». Il y a donc non seulement
l'appel de la fonction proprement dit, mais aussi chargement
du pointeur this, chargement et affectation de la valeur de
retour, et le retour lui-même. La différence dans le temps
de l'appel lui-même est donc encore plus grande.
Quand je fais ce type de tests, je fais avec:
- 0 arg + 0 ret
- 1 arg + 0 ret
- 0 arg + 1 ret
- 1 arg + 1 ret
- 2 args
Ca me donne une idee des performances plus realiste.
De l'autre côté, il faut le comparer à ce qu'on fait dans la
fonction. Dès qu'il y a une multiplication ou une division
entière, avec des termes non constantes, le temps de l'appel
devient négligeable. Sur ma machine... on ne peut pas faire
de généralités.
On ne parle que du cout de l'appel, le reste est speculatif.
C'est l'argument des utilisateurs d'Objective-C qui clame que
le message dispatch (bien que tres lent, 800% pour gcc) est
suffisament rapide en raison du code de la fonction. Et quand
tu demandes comment faire une classe Complex efficace (je te
rappelle que les data membres ne sont accessible que par des
methodes), ils te repondent que Objective-C n'est pas fait
pour ca...
Au passage j'ai une implementation de message dispatch en C
(type Objective-C) qui a un overhead de 40% a 80% par
rapport a un appel direct.
Sur quelle machine ? Mésuré comment ?
Linux IA32, gcc 3.3.5
clock_t, duree > 10 sec, elimination des couts "parasites", ratio.
C'est une implemantation qui ne supporte pas le message
forwarding avec autre chose que [jusqu'a] deux objects (idem
Stepstone objc). Cette remarque est importante car elle
"cristalise" le choix de l'implementation du dispatcher:
- soit le message dispatch recherche le pointeur de fonction a
partir du selecteur et le renvoi a l'appelant qui l'invoque
(POC, mon dispatcher version rapide). Cette techique a
l'avantage de permettre d'inliner le code de recherche dans le
cache qui avec mes tests arrive a 99,7% de hits sur 1000000 de
messages generes.
- soit tout est transfere (argument inclus), au dispatcher qui
forward le tout a la methode une fois trouvee (GCC, mon
dispatcher version lente). Cette solution permet de forwarder
n'importe quel message, au prix systematique d'un double appel
de fonction, donc performance > 200% (240-300% dans mon cas).
Ensuite tu as le choix de l'implementation du cache: GCC
utilise un sparse array a 2 (ou 3 niveaux configurable a la
compilation), le mien utilise une hash table.
Et c'est forcement plus lent qu'un method lookup. Alors j'ai
du mal a imaginer un facteur 5 pour les methodes virtuelles.
C'est cependant le cas, au moins sur les machines modernes.
Est-ce que les appels directs sont dans une TU sparee avec une
compilation separee?
Parce que un facteur 5, c'est ce que je mesure entre inline et
direct (ou virtual).
Tu ne comparais pas avec de l'inline par harsard?
D'ou ma question ;-)
Est-ce que tu as au moins lu ce que j'ai posté ? J'ai fait
Oui mais je n'ai ni le code, ni la methode de compilation.
plusieurs mésures, dans des conditions différentes. J'ai
bien l'impression que g++ a inliné les fonctions, parce
qu'il n'y a
C'est ce que je pense.
pas de différences entre les temps pour les fonctions
inlinées, et les temps pour les fonctions non-virtuelles. Et
que ces temps
Bingo...
pour les fonctions inlinées ressemblent à peu près à celles
de Sun CC. Dans le cas de Sun CC, en revanche, il y a bien
une différence.
wrote:
Les temps absolus sont peut parlant (depends des
architectures et des compilateurs). Je prefere de loin un
ratio virtuel/non-virtuel.
Mais ça aussi dépend de l'architectures et des compilateurs.
Et
Oui mais ce que l'on cherche, ce n'est pas de savoir combien
de temps met une operation, mais son efficacite par rapport a
une autre operation. C'est cela qui conditionne le
comportement des programmeurs.
beaucoup. Disons que sur une machine généraliste moderne, je
m'attendrais à un rapport entre 1:2 et 1:10. Sur des
machines plus primitives (embarquées ou anciennes), j'ai
déjà vu nettement moins de 1:2.
Jamais vu de tel rapport.
Heureusement parce que OOC n'a que des methodes virtuelles. Je
n'aurais jamais fait ca si je n'etais pas convaincu que
virtuel = direct +/- 10% (note que j'ai mesure des appels
virtuel plus rapide que des appels directs).
Je dirais que la différence entre virtuel et non-virtuel
(facteur 5), c'est à peu près ce à laquelle je m'attendrais
sur
Je n'ai jamais vu un facteur 5. J'ai vu -10%/+50% au
maximum. Et c'est coherent avec un appel indirect de
fonction via un pointeur.
On est déjà à plus de quatre ci-dessus, sur un Sparc plutôt
ancien. Des personnes de chez HP (à l'époque) m'ont assuré
que sur l'architecture PA Risc, c'était pire.
Je continue de penser que tu mesures inline vs virtual (ou
direct).
Le problème, c'est que c'est un branchement indirect. Ce qui
pose des problèmes pour la prédiction des branchements. Dans
le cas du HP, d'après ce que j'ai compris, il en résulte un
flush du pipeline à chaque coup. > > Personnellement je ne
crois pas. Cela ecroulerait les performances de > facon
mesurable et en ferait une architecture tres inefficace
(mais je > n'ai pas HP sous la main...). La plupart des
langages+projets > aujourd'hui utilisent des appels
indirects (interfaces). > Note que je ne mésure que le coup
de l'appel d'une fonction triviale (qui renvoie un int
constante) : « i = f() ; ». Il y a donc non seulement
l'appel de la fonction proprement dit, mais aussi chargement
du pointeur this, chargement et affectation de la valeur de
retour, et le retour lui-même. La différence dans le temps
de l'appel lui-même est donc encore plus grande.
Quand je fais ce type de tests, je fais avec:
- 0 arg + 0 ret
- 1 arg + 0 ret
- 0 arg + 1 ret
- 1 arg + 1 ret
- 2 args
Ca me donne une idee des performances plus realiste.
De l'autre côté, il faut le comparer à ce qu'on fait dans la
fonction. Dès qu'il y a une multiplication ou une division
entière, avec des termes non constantes, le temps de l'appel
devient négligeable. Sur ma machine... on ne peut pas faire
de généralités.
On ne parle que du cout de l'appel, le reste est speculatif.
C'est l'argument des utilisateurs d'Objective-C qui clame que
le message dispatch (bien que tres lent, 800% pour gcc) est
suffisament rapide en raison du code de la fonction. Et quand
tu demandes comment faire une classe Complex efficace (je te
rappelle que les data membres ne sont accessible que par des
methodes), ils te repondent que Objective-C n'est pas fait
pour ca...
Au passage j'ai une implementation de message dispatch en C
(type Objective-C) qui a un overhead de 40% a 80% par
rapport a un appel direct.
Sur quelle machine ? Mésuré comment ?
Linux IA32, gcc 3.3.5
clock_t, duree > 10 sec, elimination des couts "parasites", ratio.
C'est une implemantation qui ne supporte pas le message
forwarding avec autre chose que [jusqu'a] deux objects (idem
Stepstone objc). Cette remarque est importante car elle
"cristalise" le choix de l'implementation du dispatcher:
- soit le message dispatch recherche le pointeur de fonction a
partir du selecteur et le renvoi a l'appelant qui l'invoque
(POC, mon dispatcher version rapide). Cette techique a
l'avantage de permettre d'inliner le code de recherche dans le
cache qui avec mes tests arrive a 99,7% de hits sur 1000000 de
messages generes.
- soit tout est transfere (argument inclus), au dispatcher qui
forward le tout a la methode une fois trouvee (GCC, mon
dispatcher version lente). Cette solution permet de forwarder
n'importe quel message, au prix systematique d'un double appel
de fonction, donc performance > 200% (240-300% dans mon cas).
Ensuite tu as le choix de l'implementation du cache: GCC
utilise un sparse array a 2 (ou 3 niveaux configurable a la
compilation), le mien utilise une hash table.Et c'est forcement plus lent qu'un method lookup. Alors j'ai
du mal a imaginer un facteur 5 pour les methodes virtuelles.
C'est cependant le cas, au moins sur les machines modernes.
Est-ce que les appels directs sont dans une TU sparee avec une
compilation separee?
Parce que un facteur 5, c'est ce que je mesure entre inline et
direct (ou virtual).
Tu ne comparais pas avec de l'inline par harsard?
D'ou ma question ;-)Est-ce que tu as au moins lu ce que j'ai posté ? J'ai fait
Oui mais je n'ai ni le code, ni la methode de compilation.
plusieurs mésures, dans des conditions différentes. J'ai
bien l'impression que g++ a inliné les fonctions, parce
qu'il n'y a
C'est ce que je pense.pas de différences entre les temps pour les fonctions
inlinées, et les temps pour les fonctions non-virtuelles. Et
que ces temps
Bingo...pour les fonctions inlinées ressemblent à peu près à celles
de Sun CC. Dans le cas de Sun CC, en revanche, il y a bien
une différence.
writes:Parce que je constate qu'elles varient d'une exécution à
l'autre. Et que du fait que mon compteur de boucle est surn
un entier 32 bits, je ne peux pas augmenter la durée de
façon illimitée. (Normallement, je considère quelque chose
comme cinq minutes pour chaque mésure comme un minimum. Mais
ici, j'en suis loin.)
Le probleme avec une telle duree, c'est que c'est difficile
d'avoir une machine qui ne fait que ca. Le SE va prendre la
main a un moment ou a un autre, ce ui en plus va vider les
caches, etc. On mesure donc d'office plus que ce qu'on veut
mesure. J'ai pas la tete a faire une estimation.
La premiere etape a ete une interpretation de ce jeu
d'instruction par des ordinateurs ayant une architecture
interne relativement differente (et plus ou moins
specialisee). Cela peut se faire soit par interpreteur ecrit
dans le langage machine de l'architecture specialisee, soit
par une traduction plus simple (telle instruction se traduit
par telle liste de micro-instructions). Dans les deux cas, on
parle de micro-programmation et c'est parfois modifiable par
l'utilisateur. A noter qu'il peut y avoir une reelle disparite
entre l'architecture visible et l'interne. L'IBM 360 par
exemple a ete lance avec une architecture visible et 5 ou 6
architectures internes avec des performances et couts
differents.
Le problème, c'est que c'est un branchement indirect. Ce qui
pose des problèmes pour la prédiction des branchements. Dans
le cas du HP, d'après ce que j'ai compris, il en résulte un
flush du pipeline à chaque coup.
Normalement on ne vide plus les pipelines que si le
branchement n'a pas ete correctement predit. Et les taux de
prediction sont de l'ordre de 90%.
kanze@gabi-soft.fr writes:
Parce que je constate qu'elles varient d'une exécution à
l'autre. Et que du fait que mon compteur de boucle est surn
un entier 32 bits, je ne peux pas augmenter la durée de
façon illimitée. (Normallement, je considère quelque chose
comme cinq minutes pour chaque mésure comme un minimum. Mais
ici, j'en suis loin.)
Le probleme avec une telle duree, c'est que c'est difficile
d'avoir une machine qui ne fait que ca. Le SE va prendre la
main a un moment ou a un autre, ce ui en plus va vider les
caches, etc. On mesure donc d'office plus que ce qu'on veut
mesure. J'ai pas la tete a faire une estimation.
La premiere etape a ete une interpretation de ce jeu
d'instruction par des ordinateurs ayant une architecture
interne relativement differente (et plus ou moins
specialisee). Cela peut se faire soit par interpreteur ecrit
dans le langage machine de l'architecture specialisee, soit
par une traduction plus simple (telle instruction se traduit
par telle liste de micro-instructions). Dans les deux cas, on
parle de micro-programmation et c'est parfois modifiable par
l'utilisateur. A noter qu'il peut y avoir une reelle disparite
entre l'architecture visible et l'interne. L'IBM 360 par
exemple a ete lance avec une architecture visible et 5 ou 6
architectures internes avec des performances et couts
differents.
Le problème, c'est que c'est un branchement indirect. Ce qui
pose des problèmes pour la prédiction des branchements. Dans
le cas du HP, d'après ce que j'ai compris, il en résulte un
flush du pipeline à chaque coup.
Normalement on ne vide plus les pipelines que si le
branchement n'a pas ete correctement predit. Et les taux de
prediction sont de l'ordre de 90%.
writes:Parce que je constate qu'elles varient d'une exécution à
l'autre. Et que du fait que mon compteur de boucle est surn
un entier 32 bits, je ne peux pas augmenter la durée de
façon illimitée. (Normallement, je considère quelque chose
comme cinq minutes pour chaque mésure comme un minimum. Mais
ici, j'en suis loin.)
Le probleme avec une telle duree, c'est que c'est difficile
d'avoir une machine qui ne fait que ca. Le SE va prendre la
main a un moment ou a un autre, ce ui en plus va vider les
caches, etc. On mesure donc d'office plus que ce qu'on veut
mesure. J'ai pas la tete a faire une estimation.
La premiere etape a ete une interpretation de ce jeu
d'instruction par des ordinateurs ayant une architecture
interne relativement differente (et plus ou moins
specialisee). Cela peut se faire soit par interpreteur ecrit
dans le langage machine de l'architecture specialisee, soit
par une traduction plus simple (telle instruction se traduit
par telle liste de micro-instructions). Dans les deux cas, on
parle de micro-programmation et c'est parfois modifiable par
l'utilisateur. A noter qu'il peut y avoir une reelle disparite
entre l'architecture visible et l'interne. L'IBM 360 par
exemple a ete lance avec une architecture visible et 5 ou 6
architectures internes avec des performances et couts
differents.
Le problème, c'est que c'est un branchement indirect. Ce qui
pose des problèmes pour la prédiction des branchements. Dans
le cas du HP, d'après ce que j'ai compris, il en résulte un
flush du pipeline à chaque coup.
Normalement on ne vide plus les pipelines que si le
branchement n'a pas ete correctement predit. Et les taux de
prediction sont de l'ordre de 90%.
Laurent Deniau wrote:wrote:
[...]Les temps absolus sont peut parlant (depends des
architectures et des compilateurs). Je prefere de loin un
ratio virtuel/non-virtuel.Mais ça aussi dépend de l'architectures et des compilateurs.
EtOui mais ce que l'on cherche, ce n'est pas de savoir combien
de temps met une operation, mais son efficacite par rapport a
une autre operation. C'est cela qui conditionne le
comportement des programmeurs.
Oui, mais ça dépend étroitement des architectures et des
complateurs. Si je donne des mésures pour une machine donnée,
c'est assez facile à obtenir des rapports. Mais ils ne valent
que pour cette machine, dans cet environement.
Et si ça conditionne le comportement des programmeurs, il est
temps de changer les programmeurs. Considérer de telles
questions lorqu'on écrit le code, c'est vraiment de
l'optimisation prématurée.
beaucoup. Disons que sur une machine généraliste moderne, je
m'attendrais à un rapport entre 1:2 et 1:10. Sur des
machines plus primitives (embarquées ou anciennes), j'ai
déjà vu nettement moins de 1:2.Jamais vu de tel rapport.
Tu ne t'es jamais servi d'un Sparc moderne ? Où d'une machine HP
Risc ?
Heureusement parce que OOC n'a que des methodes virtuelles. Je
n'aurais jamais fait ca si je n'etais pas convaincu que
virtuel = direct +/- 10% (note que j'ai mesure des appels
virtuel plus rapide que des appels directs).
Je n'ai jamais vu une architecture où le rapport est seulement
de 10%.
Est-ce que tu es sûr que tu ne mésures que la différence de
l'appel même, et pas la différence totale de performance ? Parce
que dans les applications que je connais (même sans utiliser
inline du tout), le temps passés dans les appels n'en fait
qu'une très faible partie du temps d'exécution.
En fait, c'est quasiment impossible d'isoler l'appel
complétement -- il y a toujours au moins un retour un plus. Si
on admet que l'appel non-virtuel se fait dans un clock, mettons
2 nanosécondes, et que l'appel virtuel, à cause de la purge du
pipeline, exige en fait 15 nanosécondes, on a un rapport à 7,5.
Si on considère que par quelle miracle, le processeur arrive à
prédire le branchement correct dans le rétour, c'est 2
nanasécondes de plus : l'appel plus le rétour font alors 4
(non-virtuel) vs. 17 (virtuel) -- un rapport d'un peu plus que
4. Si le rétour purge aussi le pipeline (le cas sur les
PA-Risc), on se rétrouve avec 17 vs. 30 -- une facteur de moins
de 2 (et l'inline commence à être vraiment intéressant). Plus tu
ajoutes dans ce que tu mésures réelement, plus tu atténues la
différence.
Ce qui n'est pas forcément une mauvaise chose. En fin de compte,
ce qui compte, ce n'est pas le coût d'un appel, c'est le temps
d'exécution totale du programme. Et le fait qu'un appel
virtuelle coûte 10 fois plus qu'un appel non-virtuelle n'est pas
forcément significatif. D'abord, ils font des choses
différentes, et aussi, quel pourcentage du temps de l'exécution
se passe réelement dans les appels -- si ce n'est qu'un ou deux
pour cent, une facteur dix dans le temps d'exécution n'a pas
d'impact sur le temps d'exécution du programme. C'est le genre
de benchmark que j'ai fait par curiosité, pour m'amuser, mais
qui n'a pas le moindre impact sur la façon que je code.
Je dirais que la différence entre virtuel et non-virtuel
(facteur 5), c'est à peu près ce à laquelle je m'attendrais
surJe n'ai jamais vu un facteur 5. J'ai vu -10%/+50% au
maximum. Et c'est coherent avec un appel indirect de
fonction via un pointeur.On est déjà à plus de quatre ci-dessus, sur un Sparc plutôt
ancien. Des personnes de chez HP (à l'époque) m'ont assuré
que sur l'architecture PA Risc, c'était pire.Je continue de penser que tu mesures inline vs virtual (ou
direct).
Régarde bien les résultats avec le compilateur Sun. J'ai mésuré
inline, non-virtuel, et virtuel. Et il y a bien une différence
importante entre l'inline et le non-virtuel aussi.
En fait, je viens de régarder l'exécutable généré par g++ avec
adb ; il n'a pas générer les appels inline, et en fait, il
génère exactement la même chose que Sun CC. Mais les résultats
sont bien différents. En fin de compte, je crois que Jean-Marc a
raison quand il dit que « essayer de mesurer quelque chose de ce
genre n'a plus de sens. » Mais je suis têtu, et il s'avère qu'il
y a un certain nombre de machines qui ne font pas grand chose
aujourd'hui...Le problème, c'est que c'est un branchement indirect. Ce qui
pose des problèmes pour la prédiction des branchements. Dans
le cas du HP, d'après ce que j'ai compris, il en résulte un
flush du pipeline à chaque coup. > > Personnellement je ne
crois pas. Cela ecroulerait les performances de > facon
mesurable et en ferait une architecture tres inefficace
(mais je > n'ai pas HP sous la main...). La plupart des
langages+projets > aujourd'hui utilisent des appels
indirects (interfaces). > Note que je ne mésure que le coup
de l'appel d'une fonction triviale (qui renvoie un int
constante) : « i = f() ; ». Il y a donc non seulement
l'appel de la fonction proprement dit, mais aussi chargement
du pointeur this, chargement et affectation de la valeur de
retour, et le retour lui-même. La différence dans le temps
de l'appel lui-même est donc encore plus grande.Quand je fais ce type de tests, je fais avec:
- 0 arg + 0 ret
- 1 arg + 0 ret
- 0 arg + 1 ret
- 1 arg + 1 ret
- 2 argsCa me donne une idee des performances plus realiste.
Quand j'ai fait mes mésures, c'était par pûr curiosité. Pour
faire des mesures « réalistes », il faudrait que je fasse une
statistique de ce qui se trouve dans nos fonctions, pondérée par
la fréquence que chaque fonction est appelée.
De même, j'ai utilisé une charpente de benchmark qui n'a pas été
conçu pour mesurer des choses aussi petites. Il donne de très
bons résultats quand la durée de ce qu'on mesure dépasse la
durée d'un appel virtuel par une facteur de deux ou de trois,
mais quand la durée devient plus petite, le code qui enlève le
coût du au charpente introduit trop de jitter -- pour des choses
vraiment rapides, il m'arrive à avoir des résultats négatifs.
(Mais comment faire avec précision pour de telles durées ? Parce
que c'est précisement avec de telles durées que le coût de la
gestion de la boucle, etc., pèse le plus.)
De l'autre côté, il faut le comparer à ce qu'on fait dans la
fonction. Dès qu'il y a une multiplication ou une division
entière, avec des termes non constantes, le temps de l'appel
devient négligeable. Sur ma machine... on ne peut pas faire
de généralités.On ne parle que du cout de l'appel, le reste est speculatif.
Mais comment mesures-tu le coût de l'appel, sans mesurer aussi
le coût du retour, le coût de la gestion de la boucle, etc. ?
Sans parler du coût des indirections supplémentaires nécessaire
pour tromper l'optimisateur.
C'est l'argument des utilisateurs d'Objective-C qui clame que
le message dispatch (bien que tres lent, 800% pour gcc) est
suffisament rapide en raison du code de la fonction. Et quand
tu demandes comment faire une classe Complex efficace (je te
rappelle que les data membres ne sont accessible que par des
methodes), ils te repondent que Objective-C n'est pas fait
pour ca...
Et si tu leur démandes pourquoi Objective-C n'est pas fait pour
ça... ils répondent que c'est parce que les appels de fonction
sont trop lents ? :-)
Au passage j'ai une implementation de message dispatch en C
(type Objective-C) qui a un overhead de 40% a 80% par
rapport a un appel direct.Sur quelle machine ? Mésuré comment ?Linux IA32, gcc 3.3.5
clock_t, duree > 10 sec, elimination des couts "parasites", ratio.
La dernière fois que j'ai fait des mesures sur un Intel, c'était
un 80386, qui tournait en mode 16 bits. Alors, mes informations
ne sont pas des plus récentes. Mais c'est effectivement le
processeur où j'ai vu le moins de différence.C'est une implemantation qui ne supporte pas le message
forwarding avec autre chose que [jusqu'a] deux objects (idem
Stepstone objc). Cette remarque est importante car elle
"cristalise" le choix de l'implementation du dispatcher:- soit le message dispatch recherche le pointeur de fonction a
partir du selecteur et le renvoi a l'appelant qui l'invoque
(POC, mon dispatcher version rapide). Cette techique a
l'avantage de permettre d'inliner le code de recherche dans le
cache qui avec mes tests arrive a 99,7% de hits sur 1000000 de
messages generes.- soit tout est transfere (argument inclus), au dispatcher qui
forward le tout a la methode une fois trouvee (GCC, mon
dispatcher version lente). Cette solution permet de forwarder
n'importe quel message, au prix systematique d'un double appel
de fonction, donc performance > 200% (240-300% dans mon cas).Ensuite tu as le choix de l'implementation du cache: GCC
utilise un sparse array a 2 (ou 3 niveaux configurable a la
compilation), le mien utilise une hash table.Et c'est forcement plus lent qu'un method lookup. Alors j'ai
du mal a imaginer un facteur 5 pour les methodes virtuelles.C'est cependant le cas, au moins sur les machines modernes.Est-ce que les appels directs sont dans une TU sparee avec une
compilation separee?
Évidemment. Aussi, dans la boucle de base, il y a un appel
virtuel, pour tromper l'optimisateur. Avant de faire les
mesures, je mesure la boucle avec une fonction virtuelle vide,
et je soustrais cette mesure de chacune des mesures qui suivent.Parce que un facteur 5, c'est ce que je mesure entre inline et
direct (ou virtual).
Sur un Intel. C-à-d une architecture ancienne.Tu ne comparais pas avec de l'inline par harsard?D'ou ma question ;-)Est-ce que tu as au moins lu ce que j'ai posté ? J'ai faitOui mais je n'ai ni le code, ni la methode de compilation.
Tout à fait. Mais dans les mésures, il y avait de inline, et de
non inline. Et avec Sun CC, ils était bien différents.plusieurs mésures, dans des conditions différentes. J'ai
bien l'impression que g++ a inliné les fonctions, parce
qu'il n'y aC'est ce que je pense.pas de différences entre les temps pour les fonctions
inlinées, et les temps pour les fonctions non-virtuelles. Et
que ces tempsBingo...pour les fonctions inlinées ressemblent à peu près à celles
de Sun CC. Dans le cas de Sun CC, en revanche, il y a bien
une différence.
Tout à fait. Du coup, pour g++, il y a une facteur de 45 entre
les virtuels et les non-virtuels (ce que je trouve aussi
suspect). Avec Sun CC, en revanche, cette différence s'approche
à 5, ce qui est dans l'ordre de grandeur auquel je m'attends.
Pendant que j'écrivais ceci, j'ai repété les tests sur plusieurs
machines. Ce que je constate, c'est que les rapports entre les
résultats tiennent, sur une machine donnée. Et que curieusement,
avec g++, l'appel de la fonction non-virtuelle non-inlinée est
systèmatiquement plus rapide que l'appel inliné. Ce qui n'est
pas le cas avec Sun CC (où non-inline prend bien 7 ou 8 fois
plus de temps que inline). J'ai aussi vérifié le code de
l'exécutable avec adb -- g++ et Sun CC génère exactement les
mêmes instructions. Alors, il y a quelque chose dans la
contexte... Je ne l'explique pas, mais j'ai déjà rémarqué que
les temps sur un Sparc peuvent varier énormement selon les
alignements, etc.
Laurent Deniau wrote:
kanze@gabi-soft.fr wrote:
[...]
Les temps absolus sont peut parlant (depends des
architectures et des compilateurs). Je prefere de loin un
ratio virtuel/non-virtuel.
Mais ça aussi dépend de l'architectures et des compilateurs.
Et
Oui mais ce que l'on cherche, ce n'est pas de savoir combien
de temps met une operation, mais son efficacite par rapport a
une autre operation. C'est cela qui conditionne le
comportement des programmeurs.
Oui, mais ça dépend étroitement des architectures et des
complateurs. Si je donne des mésures pour une machine donnée,
c'est assez facile à obtenir des rapports. Mais ils ne valent
que pour cette machine, dans cet environement.
Et si ça conditionne le comportement des programmeurs, il est
temps de changer les programmeurs. Considérer de telles
questions lorqu'on écrit le code, c'est vraiment de
l'optimisation prématurée.
beaucoup. Disons que sur une machine généraliste moderne, je
m'attendrais à un rapport entre 1:2 et 1:10. Sur des
machines plus primitives (embarquées ou anciennes), j'ai
déjà vu nettement moins de 1:2.
Jamais vu de tel rapport.
Tu ne t'es jamais servi d'un Sparc moderne ? Où d'une machine HP
Risc ?
Heureusement parce que OOC n'a que des methodes virtuelles. Je
n'aurais jamais fait ca si je n'etais pas convaincu que
virtuel = direct +/- 10% (note que j'ai mesure des appels
virtuel plus rapide que des appels directs).
Je n'ai jamais vu une architecture où le rapport est seulement
de 10%.
Est-ce que tu es sûr que tu ne mésures que la différence de
l'appel même, et pas la différence totale de performance ? Parce
que dans les applications que je connais (même sans utiliser
inline du tout), le temps passés dans les appels n'en fait
qu'une très faible partie du temps d'exécution.
En fait, c'est quasiment impossible d'isoler l'appel
complétement -- il y a toujours au moins un retour un plus. Si
on admet que l'appel non-virtuel se fait dans un clock, mettons
2 nanosécondes, et que l'appel virtuel, à cause de la purge du
pipeline, exige en fait 15 nanosécondes, on a un rapport à 7,5.
Si on considère que par quelle miracle, le processeur arrive à
prédire le branchement correct dans le rétour, c'est 2
nanasécondes de plus : l'appel plus le rétour font alors 4
(non-virtuel) vs. 17 (virtuel) -- un rapport d'un peu plus que
4. Si le rétour purge aussi le pipeline (le cas sur les
PA-Risc), on se rétrouve avec 17 vs. 30 -- une facteur de moins
de 2 (et l'inline commence à être vraiment intéressant). Plus tu
ajoutes dans ce que tu mésures réelement, plus tu atténues la
différence.
Ce qui n'est pas forcément une mauvaise chose. En fin de compte,
ce qui compte, ce n'est pas le coût d'un appel, c'est le temps
d'exécution totale du programme. Et le fait qu'un appel
virtuelle coûte 10 fois plus qu'un appel non-virtuelle n'est pas
forcément significatif. D'abord, ils font des choses
différentes, et aussi, quel pourcentage du temps de l'exécution
se passe réelement dans les appels -- si ce n'est qu'un ou deux
pour cent, une facteur dix dans le temps d'exécution n'a pas
d'impact sur le temps d'exécution du programme. C'est le genre
de benchmark que j'ai fait par curiosité, pour m'amuser, mais
qui n'a pas le moindre impact sur la façon que je code.
Je dirais que la différence entre virtuel et non-virtuel
(facteur 5), c'est à peu près ce à laquelle je m'attendrais
sur
Je n'ai jamais vu un facteur 5. J'ai vu -10%/+50% au
maximum. Et c'est coherent avec un appel indirect de
fonction via un pointeur.
On est déjà à plus de quatre ci-dessus, sur un Sparc plutôt
ancien. Des personnes de chez HP (à l'époque) m'ont assuré
que sur l'architecture PA Risc, c'était pire.
Je continue de penser que tu mesures inline vs virtual (ou
direct).
Régarde bien les résultats avec le compilateur Sun. J'ai mésuré
inline, non-virtuel, et virtuel. Et il y a bien une différence
importante entre l'inline et le non-virtuel aussi.
En fait, je viens de régarder l'exécutable généré par g++ avec
adb ; il n'a pas générer les appels inline, et en fait, il
génère exactement la même chose que Sun CC. Mais les résultats
sont bien différents. En fin de compte, je crois que Jean-Marc a
raison quand il dit que « essayer de mesurer quelque chose de ce
genre n'a plus de sens. » Mais je suis têtu, et il s'avère qu'il
y a un certain nombre de machines qui ne font pas grand chose
aujourd'hui...
Le problème, c'est que c'est un branchement indirect. Ce qui
pose des problèmes pour la prédiction des branchements. Dans
le cas du HP, d'après ce que j'ai compris, il en résulte un
flush du pipeline à chaque coup. > > Personnellement je ne
crois pas. Cela ecroulerait les performances de > facon
mesurable et en ferait une architecture tres inefficace
(mais je > n'ai pas HP sous la main...). La plupart des
langages+projets > aujourd'hui utilisent des appels
indirects (interfaces). > Note que je ne mésure que le coup
de l'appel d'une fonction triviale (qui renvoie un int
constante) : « i = f() ; ». Il y a donc non seulement
l'appel de la fonction proprement dit, mais aussi chargement
du pointeur this, chargement et affectation de la valeur de
retour, et le retour lui-même. La différence dans le temps
de l'appel lui-même est donc encore plus grande.
Quand je fais ce type de tests, je fais avec:
- 0 arg + 0 ret
- 1 arg + 0 ret
- 0 arg + 1 ret
- 1 arg + 1 ret
- 2 args
Ca me donne une idee des performances plus realiste.
Quand j'ai fait mes mésures, c'était par pûr curiosité. Pour
faire des mesures « réalistes », il faudrait que je fasse une
statistique de ce qui se trouve dans nos fonctions, pondérée par
la fréquence que chaque fonction est appelée.
De même, j'ai utilisé une charpente de benchmark qui n'a pas été
conçu pour mesurer des choses aussi petites. Il donne de très
bons résultats quand la durée de ce qu'on mesure dépasse la
durée d'un appel virtuel par une facteur de deux ou de trois,
mais quand la durée devient plus petite, le code qui enlève le
coût du au charpente introduit trop de jitter -- pour des choses
vraiment rapides, il m'arrive à avoir des résultats négatifs.
(Mais comment faire avec précision pour de telles durées ? Parce
que c'est précisement avec de telles durées que le coût de la
gestion de la boucle, etc., pèse le plus.)
De l'autre côté, il faut le comparer à ce qu'on fait dans la
fonction. Dès qu'il y a une multiplication ou une division
entière, avec des termes non constantes, le temps de l'appel
devient négligeable. Sur ma machine... on ne peut pas faire
de généralités.
On ne parle que du cout de l'appel, le reste est speculatif.
Mais comment mesures-tu le coût de l'appel, sans mesurer aussi
le coût du retour, le coût de la gestion de la boucle, etc. ?
Sans parler du coût des indirections supplémentaires nécessaire
pour tromper l'optimisateur.
C'est l'argument des utilisateurs d'Objective-C qui clame que
le message dispatch (bien que tres lent, 800% pour gcc) est
suffisament rapide en raison du code de la fonction. Et quand
tu demandes comment faire une classe Complex efficace (je te
rappelle que les data membres ne sont accessible que par des
methodes), ils te repondent que Objective-C n'est pas fait
pour ca...
Et si tu leur démandes pourquoi Objective-C n'est pas fait pour
ça... ils répondent que c'est parce que les appels de fonction
sont trop lents ? :-)
Au passage j'ai une implementation de message dispatch en C
(type Objective-C) qui a un overhead de 40% a 80% par
rapport a un appel direct.
Sur quelle machine ? Mésuré comment ?
Linux IA32, gcc 3.3.5
clock_t, duree > 10 sec, elimination des couts "parasites", ratio.
La dernière fois que j'ai fait des mesures sur un Intel, c'était
un 80386, qui tournait en mode 16 bits. Alors, mes informations
ne sont pas des plus récentes. Mais c'est effectivement le
processeur où j'ai vu le moins de différence.
C'est une implemantation qui ne supporte pas le message
forwarding avec autre chose que [jusqu'a] deux objects (idem
Stepstone objc). Cette remarque est importante car elle
"cristalise" le choix de l'implementation du dispatcher:
- soit le message dispatch recherche le pointeur de fonction a
partir du selecteur et le renvoi a l'appelant qui l'invoque
(POC, mon dispatcher version rapide). Cette techique a
l'avantage de permettre d'inliner le code de recherche dans le
cache qui avec mes tests arrive a 99,7% de hits sur 1000000 de
messages generes.
- soit tout est transfere (argument inclus), au dispatcher qui
forward le tout a la methode une fois trouvee (GCC, mon
dispatcher version lente). Cette solution permet de forwarder
n'importe quel message, au prix systematique d'un double appel
de fonction, donc performance > 200% (240-300% dans mon cas).
Ensuite tu as le choix de l'implementation du cache: GCC
utilise un sparse array a 2 (ou 3 niveaux configurable a la
compilation), le mien utilise une hash table.
Et c'est forcement plus lent qu'un method lookup. Alors j'ai
du mal a imaginer un facteur 5 pour les methodes virtuelles.
C'est cependant le cas, au moins sur les machines modernes.
Est-ce que les appels directs sont dans une TU sparee avec une
compilation separee?
Évidemment. Aussi, dans la boucle de base, il y a un appel
virtuel, pour tromper l'optimisateur. Avant de faire les
mesures, je mesure la boucle avec une fonction virtuelle vide,
et je soustrais cette mesure de chacune des mesures qui suivent.
Parce que un facteur 5, c'est ce que je mesure entre inline et
direct (ou virtual).
Sur un Intel. C-à-d une architecture ancienne.
Tu ne comparais pas avec de l'inline par harsard?
D'ou ma question ;-)
Est-ce que tu as au moins lu ce que j'ai posté ? J'ai fait
Oui mais je n'ai ni le code, ni la methode de compilation.
Tout à fait. Mais dans les mésures, il y avait de inline, et de
non inline. Et avec Sun CC, ils était bien différents.
plusieurs mésures, dans des conditions différentes. J'ai
bien l'impression que g++ a inliné les fonctions, parce
qu'il n'y a
C'est ce que je pense.
pas de différences entre les temps pour les fonctions
inlinées, et les temps pour les fonctions non-virtuelles. Et
que ces temps
Bingo...
pour les fonctions inlinées ressemblent à peu près à celles
de Sun CC. Dans le cas de Sun CC, en revanche, il y a bien
une différence.
Tout à fait. Du coup, pour g++, il y a une facteur de 45 entre
les virtuels et les non-virtuels (ce que je trouve aussi
suspect). Avec Sun CC, en revanche, cette différence s'approche
à 5, ce qui est dans l'ordre de grandeur auquel je m'attends.
Pendant que j'écrivais ceci, j'ai repété les tests sur plusieurs
machines. Ce que je constate, c'est que les rapports entre les
résultats tiennent, sur une machine donnée. Et que curieusement,
avec g++, l'appel de la fonction non-virtuelle non-inlinée est
systèmatiquement plus rapide que l'appel inliné. Ce qui n'est
pas le cas avec Sun CC (où non-inline prend bien 7 ou 8 fois
plus de temps que inline). J'ai aussi vérifié le code de
l'exécutable avec adb -- g++ et Sun CC génère exactement les
mêmes instructions. Alors, il y a quelque chose dans la
contexte... Je ne l'explique pas, mais j'ai déjà rémarqué que
les temps sur un Sparc peuvent varier énormement selon les
alignements, etc.
Laurent Deniau wrote:wrote:
[...]Les temps absolus sont peut parlant (depends des
architectures et des compilateurs). Je prefere de loin un
ratio virtuel/non-virtuel.Mais ça aussi dépend de l'architectures et des compilateurs.
EtOui mais ce que l'on cherche, ce n'est pas de savoir combien
de temps met une operation, mais son efficacite par rapport a
une autre operation. C'est cela qui conditionne le
comportement des programmeurs.
Oui, mais ça dépend étroitement des architectures et des
complateurs. Si je donne des mésures pour une machine donnée,
c'est assez facile à obtenir des rapports. Mais ils ne valent
que pour cette machine, dans cet environement.
Et si ça conditionne le comportement des programmeurs, il est
temps de changer les programmeurs. Considérer de telles
questions lorqu'on écrit le code, c'est vraiment de
l'optimisation prématurée.
beaucoup. Disons que sur une machine généraliste moderne, je
m'attendrais à un rapport entre 1:2 et 1:10. Sur des
machines plus primitives (embarquées ou anciennes), j'ai
déjà vu nettement moins de 1:2.Jamais vu de tel rapport.
Tu ne t'es jamais servi d'un Sparc moderne ? Où d'une machine HP
Risc ?
Heureusement parce que OOC n'a que des methodes virtuelles. Je
n'aurais jamais fait ca si je n'etais pas convaincu que
virtuel = direct +/- 10% (note que j'ai mesure des appels
virtuel plus rapide que des appels directs).
Je n'ai jamais vu une architecture où le rapport est seulement
de 10%.
Est-ce que tu es sûr que tu ne mésures que la différence de
l'appel même, et pas la différence totale de performance ? Parce
que dans les applications que je connais (même sans utiliser
inline du tout), le temps passés dans les appels n'en fait
qu'une très faible partie du temps d'exécution.
En fait, c'est quasiment impossible d'isoler l'appel
complétement -- il y a toujours au moins un retour un plus. Si
on admet que l'appel non-virtuel se fait dans un clock, mettons
2 nanosécondes, et que l'appel virtuel, à cause de la purge du
pipeline, exige en fait 15 nanosécondes, on a un rapport à 7,5.
Si on considère que par quelle miracle, le processeur arrive à
prédire le branchement correct dans le rétour, c'est 2
nanasécondes de plus : l'appel plus le rétour font alors 4
(non-virtuel) vs. 17 (virtuel) -- un rapport d'un peu plus que
4. Si le rétour purge aussi le pipeline (le cas sur les
PA-Risc), on se rétrouve avec 17 vs. 30 -- une facteur de moins
de 2 (et l'inline commence à être vraiment intéressant). Plus tu
ajoutes dans ce que tu mésures réelement, plus tu atténues la
différence.
Ce qui n'est pas forcément une mauvaise chose. En fin de compte,
ce qui compte, ce n'est pas le coût d'un appel, c'est le temps
d'exécution totale du programme. Et le fait qu'un appel
virtuelle coûte 10 fois plus qu'un appel non-virtuelle n'est pas
forcément significatif. D'abord, ils font des choses
différentes, et aussi, quel pourcentage du temps de l'exécution
se passe réelement dans les appels -- si ce n'est qu'un ou deux
pour cent, une facteur dix dans le temps d'exécution n'a pas
d'impact sur le temps d'exécution du programme. C'est le genre
de benchmark que j'ai fait par curiosité, pour m'amuser, mais
qui n'a pas le moindre impact sur la façon que je code.
Je dirais que la différence entre virtuel et non-virtuel
(facteur 5), c'est à peu près ce à laquelle je m'attendrais
surJe n'ai jamais vu un facteur 5. J'ai vu -10%/+50% au
maximum. Et c'est coherent avec un appel indirect de
fonction via un pointeur.On est déjà à plus de quatre ci-dessus, sur un Sparc plutôt
ancien. Des personnes de chez HP (à l'époque) m'ont assuré
que sur l'architecture PA Risc, c'était pire.Je continue de penser que tu mesures inline vs virtual (ou
direct).
Régarde bien les résultats avec le compilateur Sun. J'ai mésuré
inline, non-virtuel, et virtuel. Et il y a bien une différence
importante entre l'inline et le non-virtuel aussi.
En fait, je viens de régarder l'exécutable généré par g++ avec
adb ; il n'a pas générer les appels inline, et en fait, il
génère exactement la même chose que Sun CC. Mais les résultats
sont bien différents. En fin de compte, je crois que Jean-Marc a
raison quand il dit que « essayer de mesurer quelque chose de ce
genre n'a plus de sens. » Mais je suis têtu, et il s'avère qu'il
y a un certain nombre de machines qui ne font pas grand chose
aujourd'hui...Le problème, c'est que c'est un branchement indirect. Ce qui
pose des problèmes pour la prédiction des branchements. Dans
le cas du HP, d'après ce que j'ai compris, il en résulte un
flush du pipeline à chaque coup. > > Personnellement je ne
crois pas. Cela ecroulerait les performances de > facon
mesurable et en ferait une architecture tres inefficace
(mais je > n'ai pas HP sous la main...). La plupart des
langages+projets > aujourd'hui utilisent des appels
indirects (interfaces). > Note que je ne mésure que le coup
de l'appel d'une fonction triviale (qui renvoie un int
constante) : « i = f() ; ». Il y a donc non seulement
l'appel de la fonction proprement dit, mais aussi chargement
du pointeur this, chargement et affectation de la valeur de
retour, et le retour lui-même. La différence dans le temps
de l'appel lui-même est donc encore plus grande.Quand je fais ce type de tests, je fais avec:
- 0 arg + 0 ret
- 1 arg + 0 ret
- 0 arg + 1 ret
- 1 arg + 1 ret
- 2 argsCa me donne une idee des performances plus realiste.
Quand j'ai fait mes mésures, c'était par pûr curiosité. Pour
faire des mesures « réalistes », il faudrait que je fasse une
statistique de ce qui se trouve dans nos fonctions, pondérée par
la fréquence que chaque fonction est appelée.
De même, j'ai utilisé une charpente de benchmark qui n'a pas été
conçu pour mesurer des choses aussi petites. Il donne de très
bons résultats quand la durée de ce qu'on mesure dépasse la
durée d'un appel virtuel par une facteur de deux ou de trois,
mais quand la durée devient plus petite, le code qui enlève le
coût du au charpente introduit trop de jitter -- pour des choses
vraiment rapides, il m'arrive à avoir des résultats négatifs.
(Mais comment faire avec précision pour de telles durées ? Parce
que c'est précisement avec de telles durées que le coût de la
gestion de la boucle, etc., pèse le plus.)
De l'autre côté, il faut le comparer à ce qu'on fait dans la
fonction. Dès qu'il y a une multiplication ou une division
entière, avec des termes non constantes, le temps de l'appel
devient négligeable. Sur ma machine... on ne peut pas faire
de généralités.On ne parle que du cout de l'appel, le reste est speculatif.
Mais comment mesures-tu le coût de l'appel, sans mesurer aussi
le coût du retour, le coût de la gestion de la boucle, etc. ?
Sans parler du coût des indirections supplémentaires nécessaire
pour tromper l'optimisateur.
C'est l'argument des utilisateurs d'Objective-C qui clame que
le message dispatch (bien que tres lent, 800% pour gcc) est
suffisament rapide en raison du code de la fonction. Et quand
tu demandes comment faire une classe Complex efficace (je te
rappelle que les data membres ne sont accessible que par des
methodes), ils te repondent que Objective-C n'est pas fait
pour ca...
Et si tu leur démandes pourquoi Objective-C n'est pas fait pour
ça... ils répondent que c'est parce que les appels de fonction
sont trop lents ? :-)
Au passage j'ai une implementation de message dispatch en C
(type Objective-C) qui a un overhead de 40% a 80% par
rapport a un appel direct.Sur quelle machine ? Mésuré comment ?Linux IA32, gcc 3.3.5
clock_t, duree > 10 sec, elimination des couts "parasites", ratio.
La dernière fois que j'ai fait des mesures sur un Intel, c'était
un 80386, qui tournait en mode 16 bits. Alors, mes informations
ne sont pas des plus récentes. Mais c'est effectivement le
processeur où j'ai vu le moins de différence.C'est une implemantation qui ne supporte pas le message
forwarding avec autre chose que [jusqu'a] deux objects (idem
Stepstone objc). Cette remarque est importante car elle
"cristalise" le choix de l'implementation du dispatcher:- soit le message dispatch recherche le pointeur de fonction a
partir du selecteur et le renvoi a l'appelant qui l'invoque
(POC, mon dispatcher version rapide). Cette techique a
l'avantage de permettre d'inliner le code de recherche dans le
cache qui avec mes tests arrive a 99,7% de hits sur 1000000 de
messages generes.- soit tout est transfere (argument inclus), au dispatcher qui
forward le tout a la methode une fois trouvee (GCC, mon
dispatcher version lente). Cette solution permet de forwarder
n'importe quel message, au prix systematique d'un double appel
de fonction, donc performance > 200% (240-300% dans mon cas).Ensuite tu as le choix de l'implementation du cache: GCC
utilise un sparse array a 2 (ou 3 niveaux configurable a la
compilation), le mien utilise une hash table.Et c'est forcement plus lent qu'un method lookup. Alors j'ai
du mal a imaginer un facteur 5 pour les methodes virtuelles.C'est cependant le cas, au moins sur les machines modernes.Est-ce que les appels directs sont dans une TU sparee avec une
compilation separee?
Évidemment. Aussi, dans la boucle de base, il y a un appel
virtuel, pour tromper l'optimisateur. Avant de faire les
mesures, je mesure la boucle avec une fonction virtuelle vide,
et je soustrais cette mesure de chacune des mesures qui suivent.Parce que un facteur 5, c'est ce que je mesure entre inline et
direct (ou virtual).
Sur un Intel. C-à-d une architecture ancienne.Tu ne comparais pas avec de l'inline par harsard?D'ou ma question ;-)Est-ce que tu as au moins lu ce que j'ai posté ? J'ai faitOui mais je n'ai ni le code, ni la methode de compilation.
Tout à fait. Mais dans les mésures, il y avait de inline, et de
non inline. Et avec Sun CC, ils était bien différents.plusieurs mésures, dans des conditions différentes. J'ai
bien l'impression que g++ a inliné les fonctions, parce
qu'il n'y aC'est ce que je pense.pas de différences entre les temps pour les fonctions
inlinées, et les temps pour les fonctions non-virtuelles. Et
que ces tempsBingo...pour les fonctions inlinées ressemblent à peu près à celles
de Sun CC. Dans le cas de Sun CC, en revanche, il y a bien
une différence.
Tout à fait. Du coup, pour g++, il y a une facteur de 45 entre
les virtuels et les non-virtuels (ce que je trouve aussi
suspect). Avec Sun CC, en revanche, cette différence s'approche
à 5, ce qui est dans l'ordre de grandeur auquel je m'attends.
Pendant que j'écrivais ceci, j'ai repété les tests sur plusieurs
machines. Ce que je constate, c'est que les rapports entre les
résultats tiennent, sur une machine donnée. Et que curieusement,
avec g++, l'appel de la fonction non-virtuelle non-inlinée est
systèmatiquement plus rapide que l'appel inliné. Ce qui n'est
pas le cas avec Sun CC (où non-inline prend bien 7 ou 8 fois
plus de temps que inline). J'ai aussi vérifié le code de
l'exécutable avec adb -- g++ et Sun CC génère exactement les
mêmes instructions. Alors, il y a quelque chose dans la
contexte... Je ne l'explique pas, mais j'ai déjà rémarqué que
les temps sur un Sparc peuvent varier énormement selon les
alignements, etc.
gcc (direct, indirect) 0 = no arg
call0 : time = 3.03e-09 sec, iter = 330033003/sec)
call1 : time = 3.04e-09 sec, iter = 328947368/sec)
call2 : time = 4.70e-09 sec, iter = 212765957/sec)
callR : time = 3.72e-09 sec, iter = 268817204/sec)
callR1 : time = 3.38e-09 sec, iter = 295857988/sec)
icall0 : time = 4.05e-09 sec, iter = 246913580/sec)
icall1 : time = 6.22e-09 sec, iter = 160771704/sec)
icall2 : time = 4.73e-09 sec, iter = 211416490/sec)
icallR : time = 4.72e-09 sec, iter = 211864406/sec)
icallR1: time = 5.48e-09 sec, iter = 182481751/sec)
Les appels directs sont plus rapide.
gcc (direct, indirect) 0 = no arg
call0 : time = 3.03e-09 sec, iter = 330033003/sec)
call1 : time = 3.04e-09 sec, iter = 328947368/sec)
call2 : time = 4.70e-09 sec, iter = 212765957/sec)
callR : time = 3.72e-09 sec, iter = 268817204/sec)
callR1 : time = 3.38e-09 sec, iter = 295857988/sec)
icall0 : time = 4.05e-09 sec, iter = 246913580/sec)
icall1 : time = 6.22e-09 sec, iter = 160771704/sec)
icall2 : time = 4.73e-09 sec, iter = 211416490/sec)
icallR : time = 4.72e-09 sec, iter = 211864406/sec)
icallR1: time = 5.48e-09 sec, iter = 182481751/sec)
Les appels directs sont plus rapide.
gcc (direct, indirect) 0 = no arg
call0 : time = 3.03e-09 sec, iter = 330033003/sec)
call1 : time = 3.04e-09 sec, iter = 328947368/sec)
call2 : time = 4.70e-09 sec, iter = 212765957/sec)
callR : time = 3.72e-09 sec, iter = 268817204/sec)
callR1 : time = 3.38e-09 sec, iter = 295857988/sec)
icall0 : time = 4.05e-09 sec, iter = 246913580/sec)
icall1 : time = 6.22e-09 sec, iter = 160771704/sec)
icall2 : time = 4.73e-09 sec, iter = 211416490/sec)
icallR : time = 4.72e-09 sec, iter = 211864406/sec)
icallR1: time = 5.48e-09 sec, iter = 182481751/sec)
Les appels directs sont plus rapide.
Laurent Deniau writes:gcc (direct, indirect) 0 = no arg
call0 : time = 3.03e-09 sec, iter = 330033003/sec)
call1 : time = 3.04e-09 sec, iter = 328947368/sec)
call2 : time = 4.70e-09 sec, iter = 212765957/sec)
callR : time = 3.72e-09 sec, iter = 268817204/sec)
callR1 : time = 3.38e-09 sec, iter = 295857988/sec)
icall0 : time = 4.05e-09 sec, iter = 246913580/sec)
icall1 : time = 6.22e-09 sec, iter = 160771704/sec)
icall2 : time = 4.73e-09 sec, iter = 211416490/sec)
icallR : time = 4.72e-09 sec, iter = 211864406/sec)
icallR1: time = 5.48e-09 sec, iter = 182481751/sec)
Les appels directs sont plus rapide.
Pour info:
Sun-Blade-1500
Avec sparcWorks, -xO3 si j'ai bonne mémoire mais rien de
call0 : time = 5.24e-09 sec, iter = 190839694/sec)
call1 : time = 6.17e-09 sec, iter = 162074554/sec)
call2 : time = 6.20e-09 sec, iter = 161290322/sec)
callR : time = 5.28e-09 sec, iter = 189393939/sec)
callR1 : time = 6.20e-09 sec, iter = 161290322/sec)
icall0 : time = 1.38e-08 sec, iter = 72202166/sec)
icall1 : time = 1.38e-08 sec, iter = 72254335/sec)
icall2 : time = 1.34e-08 sec, iter = 74571215/sec)
icallR : time = 1.38e-08 sec, iter = 72411296/sec)
icallR1: time = 1.34e-08 sec, iter = 74794315/sec)
Sun-Blade-2500
call0 : time = 4.39e-09 sec, iter = 227790432/sec)
call1 : time = 5.12e-09 sec, iter = 195312499/sec)
call2 : time = 5.09e-09 sec, iter = 196463654/sec)
callR : time = 4.33e-09 sec, iter = 230946882/sec)
callR1 : time = 5.15e-09 sec, iter = 194174757/sec)
icall0 : time = 1.14e-08 sec, iter = 87950747/sec)
icall1 : time = 1.14e-08 sec, iter = 87719298/sec)
icall2 : time = 1.10e-08 sec, iter = 90991810/sec)
icallR : time = 1.14e-08 sec, iter = 87873462/sec)
icallR1: time = 1.10e-08 sec, iter = 90661831/sec)
(Dans les deux cas c'est des Sparc IIIi; donc si j'ai bonne memoire
multi-scalaire mais execution dans l'ordre; il me semble que les
Sparc IV ont de l'execution dans le desordre et speculative.)
Laurent Deniau <Laurent.Deniau@cern.ch> writes:
gcc (direct, indirect) 0 = no arg
call0 : time = 3.03e-09 sec, iter = 330033003/sec)
call1 : time = 3.04e-09 sec, iter = 328947368/sec)
call2 : time = 4.70e-09 sec, iter = 212765957/sec)
callR : time = 3.72e-09 sec, iter = 268817204/sec)
callR1 : time = 3.38e-09 sec, iter = 295857988/sec)
icall0 : time = 4.05e-09 sec, iter = 246913580/sec)
icall1 : time = 6.22e-09 sec, iter = 160771704/sec)
icall2 : time = 4.73e-09 sec, iter = 211416490/sec)
icallR : time = 4.72e-09 sec, iter = 211864406/sec)
icallR1: time = 5.48e-09 sec, iter = 182481751/sec)
Les appels directs sont plus rapide.
Pour info:
Sun-Blade-1500
Avec sparcWorks, -xO3 si j'ai bonne mémoire mais rien de
call0 : time = 5.24e-09 sec, iter = 190839694/sec)
call1 : time = 6.17e-09 sec, iter = 162074554/sec)
call2 : time = 6.20e-09 sec, iter = 161290322/sec)
callR : time = 5.28e-09 sec, iter = 189393939/sec)
callR1 : time = 6.20e-09 sec, iter = 161290322/sec)
icall0 : time = 1.38e-08 sec, iter = 72202166/sec)
icall1 : time = 1.38e-08 sec, iter = 72254335/sec)
icall2 : time = 1.34e-08 sec, iter = 74571215/sec)
icallR : time = 1.38e-08 sec, iter = 72411296/sec)
icallR1: time = 1.34e-08 sec, iter = 74794315/sec)
Sun-Blade-2500
call0 : time = 4.39e-09 sec, iter = 227790432/sec)
call1 : time = 5.12e-09 sec, iter = 195312499/sec)
call2 : time = 5.09e-09 sec, iter = 196463654/sec)
callR : time = 4.33e-09 sec, iter = 230946882/sec)
callR1 : time = 5.15e-09 sec, iter = 194174757/sec)
icall0 : time = 1.14e-08 sec, iter = 87950747/sec)
icall1 : time = 1.14e-08 sec, iter = 87719298/sec)
icall2 : time = 1.10e-08 sec, iter = 90991810/sec)
icallR : time = 1.14e-08 sec, iter = 87873462/sec)
icallR1: time = 1.10e-08 sec, iter = 90661831/sec)
(Dans les deux cas c'est des Sparc IIIi; donc si j'ai bonne memoire
multi-scalaire mais execution dans l'ordre; il me semble que les
Sparc IV ont de l'execution dans le desordre et speculative.)
Laurent Deniau writes:gcc (direct, indirect) 0 = no arg
call0 : time = 3.03e-09 sec, iter = 330033003/sec)
call1 : time = 3.04e-09 sec, iter = 328947368/sec)
call2 : time = 4.70e-09 sec, iter = 212765957/sec)
callR : time = 3.72e-09 sec, iter = 268817204/sec)
callR1 : time = 3.38e-09 sec, iter = 295857988/sec)
icall0 : time = 4.05e-09 sec, iter = 246913580/sec)
icall1 : time = 6.22e-09 sec, iter = 160771704/sec)
icall2 : time = 4.73e-09 sec, iter = 211416490/sec)
icallR : time = 4.72e-09 sec, iter = 211864406/sec)
icallR1: time = 5.48e-09 sec, iter = 182481751/sec)
Les appels directs sont plus rapide.
Pour info:
Sun-Blade-1500
Avec sparcWorks, -xO3 si j'ai bonne mémoire mais rien de
call0 : time = 5.24e-09 sec, iter = 190839694/sec)
call1 : time = 6.17e-09 sec, iter = 162074554/sec)
call2 : time = 6.20e-09 sec, iter = 161290322/sec)
callR : time = 5.28e-09 sec, iter = 189393939/sec)
callR1 : time = 6.20e-09 sec, iter = 161290322/sec)
icall0 : time = 1.38e-08 sec, iter = 72202166/sec)
icall1 : time = 1.38e-08 sec, iter = 72254335/sec)
icall2 : time = 1.34e-08 sec, iter = 74571215/sec)
icallR : time = 1.38e-08 sec, iter = 72411296/sec)
icallR1: time = 1.34e-08 sec, iter = 74794315/sec)
Sun-Blade-2500
call0 : time = 4.39e-09 sec, iter = 227790432/sec)
call1 : time = 5.12e-09 sec, iter = 195312499/sec)
call2 : time = 5.09e-09 sec, iter = 196463654/sec)
callR : time = 4.33e-09 sec, iter = 230946882/sec)
callR1 : time = 5.15e-09 sec, iter = 194174757/sec)
icall0 : time = 1.14e-08 sec, iter = 87950747/sec)
icall1 : time = 1.14e-08 sec, iter = 87719298/sec)
icall2 : time = 1.10e-08 sec, iter = 90991810/sec)
icallR : time = 1.14e-08 sec, iter = 87873462/sec)
icallR1: time = 1.10e-08 sec, iter = 90661831/sec)
(Dans les deux cas c'est des Sparc IIIi; donc si j'ai bonne memoire
multi-scalaire mais execution dans l'ordre; il me semble que les
Sparc IV ont de l'execution dans le desordre et speculative.)
Juste un détail, mais le microcode est venu assez
tardivement, dans les années 1960. En fait, l'IBM 360
était la première architecture de s'en servir. À cet
égard, les architectures RISC sont un rétour en arrière.
[...]Le problème, c'est que c'est un branchement indirect. Ce qui
pose des problèmes pour la prédiction des branchements. Dans
le cas du HP, d'après ce que j'ai compris, il en résulte un
flush du pipeline à chaque coup.
Normalement on ne vide plus les pipelines que si le
branchement n'a pas ete correctement predit. Et les taux de
prediction sont de l'ordre de 90%.
C'est bien d'un employé de HP (à l'époque) que j'ai mes
informations. Que les PA Risc (de l'époque) n'essayait même pas
à faire de la prédiction sur un saut indirect.
Juste un détail, mais le microcode est venu assez
tardivement, dans les années 1960. En fait, l'IBM 360
était la première architecture de s'en servir. À cet
égard, les architectures RISC sont un rétour en arrière.
[...]
Le problème, c'est que c'est un branchement indirect. Ce qui
pose des problèmes pour la prédiction des branchements. Dans
le cas du HP, d'après ce que j'ai compris, il en résulte un
flush du pipeline à chaque coup.
Normalement on ne vide plus les pipelines que si le
branchement n'a pas ete correctement predit. Et les taux de
prediction sont de l'ordre de 90%.
C'est bien d'un employé de HP (à l'époque) que j'ai mes
informations. Que les PA Risc (de l'époque) n'essayait même pas
à faire de la prédiction sur un saut indirect.
Juste un détail, mais le microcode est venu assez
tardivement, dans les années 1960. En fait, l'IBM 360
était la première architecture de s'en servir. À cet
égard, les architectures RISC sont un rétour en arrière.
[...]Le problème, c'est que c'est un branchement indirect. Ce qui
pose des problèmes pour la prédiction des branchements. Dans
le cas du HP, d'après ce que j'ai compris, il en résulte un
flush du pipeline à chaque coup.
Normalement on ne vide plus les pipelines que si le
branchement n'a pas ete correctement predit. Et les taux de
prediction sont de l'ordre de 90%.
C'est bien d'un employé de HP (à l'époque) que j'ai mes
informations. Que les PA Risc (de l'époque) n'essayait même pas
à faire de la prédiction sur un saut indirect.