La conclusion ici:
Les compilateurs Fortran sont-ils vraiment meilleurs?
est que gfortran et gcc sont aussi rapides pour le code simple. Je voulais donc essayer quelque chose de plus compliqué. J'ai pris l'exemple de la fusillade de la norme spectrale. Je précalcule d'abord la matrice 2D A (:, :), puis calcule la norme. (Je pense que cette solution n'est pas autorisée lors de la fusillade.) J'ai implémenté la version Fortran et C. Voici le code:
https://github.com/certik/spectral_norm
Les versions les plus rapides de gfortran sont spectral_norm2.f90 et spectral_norm6.f90 (l'une utilise les matmul et dot_product intégrés de Fortran, l'autre implémente ces deux fonctions dans le code - sans différence de vitesse). Le code C / C ++ le plus rapide que j'ai pu écrire est spectral_norm7.cpp. Les horaires à partir de la version git 457d9d9 sur mon ordinateur portable sont les suivants:
$ time ./spectral_norm6 5500
1.274224153
real 0m2.675s
user 0m2.520s
sys 0m0.132s
$ time ./spectral_norm7 5500
1.274224153
real 0m2.871s
user 0m2.724s
sys 0m0.124s
La version de gfortran est donc un peu plus rapide. Pourquoi donc? Si vous envoyez une demande d'extraction avec une implémentation C plus rapide (ou collez simplement un code), je mettrai à jour le référentiel.
Dans Fortran, je passe un tableau 2D, tandis que dans CI, j'utilise un tableau 1D. N'hésitez pas à utiliser un tableau 2D ou tout autre moyen que vous jugerez utile.
Quant aux compilateurs, comparons gcc vs gfortran, icc vs ifort, et ainsi de suite. (Contrairement à la page de shootout, qui compare ifort vs gcc.)
Mise à jour : en utilisant la version 179dae2, qui améliore matmul3 () dans ma version C, ils sont désormais aussi rapides:
$ time ./spectral_norm6 5500
1.274224153
real 0m2.669s
user 0m2.500s
sys 0m0.144s
$ time ./spectral_norm7 5500
1.274224153
real 0m2.665s
user 0m2.472s
sys 0m0.168s
La version vectorisée de Pedro ci-dessous est plus rapide:
$ time ./spectral_norm8 5500
1.274224153
real 0m2.523s
user 0m2.336s
sys 0m0.156s
Enfin, comme le rapporte laxxy ci-dessous pour les compilateurs Intel, il ne semble pas y avoir de grande différence et même le code Fortran le plus simple (spectral_norm1) est parmi les plus rapides.
Réponses:
Tout d'abord, merci d'avoir posté cette question / défi! Comme avertissement, je suis un programmeur C natif avec une certaine expérience de Fortran, et je me sens plus à l'aise en C, donc en tant que tel, je me concentrerai uniquement sur l'amélioration de la version C. J'invite tous les hacks de Fortran à essayer aussi!
Juste pour rappeler aux nouveaux arrivants de quoi il s'agit: la prémisse de base dans ce fil était que gcc / fortran et icc / ifort devraient, puisqu'ils ont respectivement les mêmes back-ends, produire un code équivalent pour le même programme (sémantiquement identique), indépendamment d'être en C ou Fortran. La qualité du résultat ne dépend que de la qualité des implémentations respectives.
J'ai joué un peu avec le code et sur mon ordinateur (ThinkPad 201x, Intel Core i5 M560, 2,67 GHz), en utilisant
gcc
4.6.1 et les drapeaux de compilation suivants:Je suis aussi allé de l' avant et écrit un SIMD vectorisé version langage C du code C ++,
spectral_norm_vec.c
:Les trois versions ont été compilées avec les mêmes drapeaux et la même
gcc
version. Notez que j'ai enveloppé l'appel de fonction principale dans une boucle de 0 à 9 pour obtenir des timings plus précis.Ainsi, avec de «meilleurs» indicateurs de compilateur, la version C ++ surpasse la version Fortran et les boucles vectorisées codées à la main ne fournissent qu'une amélioration marginale. Un rapide coup d'œil à l'assembleur de la version C ++ montre que les boucles principales ont également été vectorisées, bien que déroulées de manière plus agressive.
J'ai également regardé l'assembleur généré par
gfortran
et voici la grande surprise: pas de vectorisation. J'attribue le fait qu'il n'est que légèrement plus lent au problème de la limitation de la bande passante, du moins sur mon architecture. Pour chacune des multiplications matricielles, 230 Mo de données sont parcourus, ce qui submerge à peu près tous les niveaux de cache. Si vous utilisez une valeur d'entrée plus petite, par exemple100
, les différences de performances augmentent considérablement.En guise de remarque, au lieu d'obséder par la vectorisation, l'alignement et les drapeaux du compilateur, l'optimisation la plus évidente serait de calculer les premières itérations en arithmétique simple précision, jusqu'à ce que nous ayons ~ 8 chiffres du résultat. Les instructions en simple précision sont non seulement plus rapides, mais la quantité de mémoire à déplacer est également divisée par deux.
la source
gcc
/gfortran
utilisez-vous? Dans les discussions précédentes, différentes versions ont donné des résultats sensiblement différents.matmul2
dans la version Fortran est sémantiquement équivalent àmatmul3
dans ma version C. Les deux versions sont vraiment maintenant les mêmes et doncgcc
/gfortran
devraient produire les mêmes résultats pour les deux, par exemple, aucun front-end / langue n'est meilleur que l'autre dans ce cas.gcc
a juste l'avantage que nous pourrions exploiter des instructions vectorisées si nous le choisissons.vector_size
attribut afin de rendre le code indépendant de la plate-forme, c'est-à-dire que l'utilisation de cette syntaxegcc
devrait pouvoir générer du code vectorisé pour d'autres plates-formes, par exemple en utilisant AltiVec sur l'architecture IBM Power.La réponse de user389 a été supprimée mais permettez-moi de dire que je suis fermement dans son camp: je ne vois pas ce que nous apprenons en comparant les micro-benchmarks dans différentes langues. Cela ne me surprend pas autant que C et Fortran obtiennent à peu près les mêmes performances sur cette référence, étant donné sa courte durée. Mais la référence est également ennuyeuse car elle peut facilement être écrite dans les deux langues en quelques dizaines de lignes. D'un point de vue logiciel, ce n'est pas un cas représentatif: nous devons nous soucier des logiciels qui ont 10 000 ou 100 000 lignes de code et comment les compilateurs le font. Bien sûr, à cette échelle, on découvrira rapidement d'autres choses: que la langue A nécessite 10 000 lignes tandis que la langue B en requiert 50 000. Ou l'inverse, selon ce que vous voulez faire. Et soudain ça '
En d'autres termes, peu m'importe que mon application puisse être 50% plus rapide si je la développais dans Fortran 77 si au lieu de cela il ne me faudrait qu'un mois pour qu'elle fonctionne correctement alors que cela me prendrait 3 mois en F77. Le problème avec la question ici est qu'elle se concentre sur un aspect (noyaux individuels) qui n'est pas pertinent dans la pratique à mon avis.
la source
Il s'avère que je peux écrire un code Python (en utilisant numpy pour effectuer les opérations BLAS) plus rapidement que le code Fortran compilé avec le compilateur gfortran de mon système.
foo1.py:
et sn6a.f90, un spectral_norm6.f90 très légèrement modifié:
la source
Vérifié cela avec des compilateurs Intel. Avec 11.1 (-fast, impliquant -O3), et avec 12.0 (-O2) les plus rapides sont 1,2,6,7 et 8 (c'est-à-dire les codes Fortran et C "les plus simples", et le C vectorisé à la main) - ceux-ci sont indiscernables les uns des autres à ~ 1,5 s. Les tests 3 et 5 (avec tableau comme fonction) sont plus lents; # 4 Je n'ai pas pu compiler.
Tout particulièrement, si vous compilez avec 12.0 et -O3, plutôt que -O2, les 2 premiers codes Fortran ("les plus simples") ralentissent BEAUCOUP (1.5 -> 10.2 sec.) - ce n'est pas la première fois que je vois quelque chose comme cela, mais cela peut être l'exemple le plus dramatique. Si c'est toujours le cas dans la version actuelle, je pense que ce serait une bonne idée de le signaler à Intel, car il y a clairement quelque chose qui va très mal avec leurs optimisations dans ce cas plutôt simple.
Sinon, je suis d'accord avec Jonathan que ce n'est pas un exercice particulièrement instructif :)
la source