À quel point les compilateurs Fortran sont-ils vraiment meilleurs?

74

Cette question est une extension de deux discussions qui ont récemment été abordées dans les réponses à " C ++ vs Fortran pour HPC ". Et c'est un peu plus un défi qu'une question ...

L'un des arguments les plus souvent entendus en faveur de Fortran est que les compilateurs sont simplement meilleurs. Étant donné que la plupart des compilateurs C / Fortran partagent le même back-end, le code généré pour les programmes sémantiquement équivalents dans les deux langues doit être identique. On pourrait toutefois affirmer que C / Fortran est plus / moins facile à optimiser pour le compilateur.

J'ai donc décidé d'essayer un test simple: j'ai reçu une copie de daxpy.f et daxpy.c et les ai compilés avec gfortran / gcc.

Maintenant, daxpy.c n’est qu’une traduction f2c de daxpy.f (code généré automatiquement, moche comme heck), j’ai donc pris ce code et l’a nettoyé un peu (meet daxpy_c), ce qui signifiait essentiellement réécrire la boucle la plus intérieure

for ( i = 0 ; i < n ; i++ )
    dy[i] += da * dx[i];

Enfin, je l'ai réécrit (entrez daxpy_cvec) en utilisant la syntaxe vectorielle de gcc:

#define vector(elcount, type)  __attribute__((vector_size((elcount)*sizeof(type)))) type
vector(2,double) va = { da , da }, *vx, *vy;

vx = (void *)dx; vy = (void *)dy;
for ( i = 0 ; i < (n/2 & ~1) ; i += 2 ) {
    vy[i] += va * vx[i];
    vy[i+1] += va * vx[i+1];
    }
for ( i = n & ~3 ; i < n ; i++ )
    dy[i] += da * dx[i];

Notez que j'utilise des vecteurs de longueur 2 (c'est tout ce que permet SSE2) et que je traite deux vecteurs à la fois. En effet, sur de nombreuses architectures, nous pouvons avoir plus d'unités de multiplication que d'éléments vectoriels.

Tous les codes ont été compilés avec la version 4.5 de gfortran / gcc avec les indicateurs "-O3 -Wall -msse2 -march = native -fast-math -fomit-frame-pointer -malign-double -fstrict-aliasing". Sur mon ordinateur portable (processeur Intel Core i5, M560, 2,67 GHz), j'ai la sortie suivante:

pedro@laika:~/work/fvsc$ ./test 1000000 10000
timing 1000000 runs with a vector of length 10000.
daxpy_f took 8156.7 ms.
daxpy_f2c took 10568.1 ms.
daxpy_c took 7912.8 ms.
daxpy_cvec took 5670.8 ms.

Donc, le code Fortran original prend un peu plus de 8,1 secondes, sa traduction automatique prend 10,5 secondes, l'implémentation C naïve le fait en 7.9 et le code explicitement vectorisé le fait en 5.6, légèrement moins.

Fortran est légèrement plus lent que l'implémentation naïve du C et 50% moins rapide que l'implémentation du C vectorisé.

Alors voici la question: je suis un programmeur C natif et je suis donc plutôt confiant d'avoir fait du bon travail sur ce code, mais le code Fortran a été touché pour la dernière fois en 1993 et ​​pourrait donc être un peu obsolète. Puisque je ne me sens pas aussi à l'aise de coder en Fortran que d'autres ici, est-ce que quelqu'un peut faire un meilleur travail, c'est-à-dire être plus compétitif que n'importe laquelle des deux versions C?

De plus, quelqu'un peut-il essayer ce test avec icc / ifort? La syntaxe vectorielle ne fonctionnera probablement pas, mais je serais curieux de voir comment la version naïve du C se comporte ici. Il en va de même pour les personnes avec xlc / xlf qui traînent.

J'ai téléchargé les sources et un Makefile ici . Pour obtenir des synchronisations précises, définissez CPU_TPS dans test.c sur le nombre de Hz de votre CPU. Si vous trouvez des améliorations dans les versions, merci de les poster ici!

Mise à jour:

J'ai ajouté le code de test de stali aux fichiers en ligne et l'ai complété avec une version C. J'ai modifié les programmes pour faire 1'000'000 boucles sur des vecteurs de longueur 10'000 afin d'être cohérents avec le test précédent (et parce que ma machine ne pouvait pas affecter des vecteurs de longueur 1'000'000'000, comme dans l'original de stali code). Puisque les nombres sont maintenant un peu plus petits, j'ai utilisé l'option -par-threshold:50pour rendre le compilateur plus susceptible de se mettre en parallèle. La version icc / ifort utilisée est la 12.1.2 20111128 et les résultats sont les suivants

pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=1 time ./icctest_c
3.27user 0.00system 0:03.27elapsed 99%CPU

pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=1 time ./icctest_f
3.29user 0.00system 0:03.29elapsed 99%CPU

pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=2 time ./icctest_c
4.89user 0.00system 0:02.60elapsed 188%CPU

pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=2 time ./icctest_f
4.91user 0.00system 0:02.60elapsed 188%CPU

En résumé, les résultats sont, à toutes fins pratiques, identiques pour les versions C et Fortran, et les deux codes se parallélisent automagiquement. Notez que les temps rapides comparés au test précédent sont dus à l'utilisation de l'arithmétique à virgule flottante simple précision!

Mise à jour:

Bien que je n'aime pas trop le fardeau de la preuve ici, j'ai recodifié l' exemple de multiplication de matrice de stali en C et l'a ajouté aux fichiers sur le Web . Voici les résultats de la boucle triple pour un et deux processeurs:

pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=1 time ./mm_test_f 2500
 triple do time   3.46421700000000     
3.63user 0.06system 0:03.70elapsed 99%CPU

pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=1 time ./mm_test_c 2500
triple do time 3.431997791385768
3.58user 0.10system 0:03.69elapsed 99%CPU

pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=2 time ./mm_test_f 2500
 triple do time   5.09631900000000     
5.26user 0.06system 0:02.81elapsed 189%CPU

pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=2 time ./mm_test_c 2500
triple do time 2.298916975280899
4.78user 0.08system 0:02.62elapsed 184%CPU

Notez que cpu_timedans les mesures Fortran, le temps de calcul et pas l’horloge murale, c’est pourquoi j’ai encapsulé les appels timepour les comparer entre deux processeurs. Il n'y a pas de réelle différence entre les résultats, sauf que la version C fait un peu mieux sur deux cœurs.

Maintenant pour la matmulcommande, bien sûr seulement en Fortran car cet élément intrinsèque n’est pas disponible en C:

pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=1 time ./mm_test_f 2500
 matmul    time   23.6494780000000     
23.80user 0.08system 0:23.91elapsed 99%CPU

pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=2 time ./mm_test_f 2500
 matmul    time   26.6176640000000     
26.75user 0.10system 0:13.62elapsed 197%CPU

Sensationnel. C'est absolument terrible. Quelqu'un peut-il savoir ce que je fais de mal ou expliquer pourquoi cet élément intrinsèque est encore en quelque sorte une bonne chose?

Je n'ai pas ajouté les dgemmappels à la référence car il s'agit d'appels de bibliothèque ayant la même fonction dans Intel MKL.

Pour les prochains tests, quelqu'un peut-il suggérer un exemple connu pour être plus lent en C que dans Fortran?

Mise à jour

Pour vérifier l'affirmation de Stali selon laquelle l' matmulintrinsèque est "un ordre de grandeur" plus rapidement que le produit matriciel explicite sur des matrices plus petites, j'ai modifié son propre code pour multiplier les matrices de taille 100x100 en utilisant les deux méthodes, 10 000 fois chacune. Les résultats, sur un et deux processeurs, sont les suivants:

pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=1 time ./mm_test_f 10000 100
 matmul    time   3.61222500000000     
 triple do time   3.54022200000000     
7.15user 0.00system 0:07.16elapsed 99%CPU

pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=2 time ./mm_test_f 10000 100
 matmul    time   4.54428400000000     
 triple do time   4.31626900000000     
8.86user 0.00system 0:04.60elapsed 192%CPU

Mise à jour

Grisu a raison de souligner que, sans optimisation, gcc convertit les opérations sur les nombres complexes en appels de fonctions de bibliothèque, tandis que gfortran les insère dans quelques instructions.

Le compilateur C générera le même code compact si l'option -fcx-limited-rangeest définie, c'est-à-dire que le compilateur est invité à ignorer les potentiels de dépassement / dépassement de capacité dans les valeurs intermédiaires. Cette option est en quelque sorte définie par défaut dans gfortran et peut conduire à des résultats incorrects. Forcer -fno-cx-limited-rangeà Gfortran n'a rien changé.

Donc, c’est vraiment un argument contre l’ utilisation de gfortran pour les calculs numériques: Les opérations sur des valeurs complexes peuvent dépasser / dépasser le flux même si les résultats corrects se situent dans la plage de virgule flottante. C'est en fait une norme Fortran. Dans gcc, ou dans C99 en général, le comportement par défaut consiste à agir strictement (lire conforme à la norme IEEE-754), sauf indication contraire.

Rappel: Veuillez garder à l’esprit que la question principale était de savoir si les compilateurs Fortran produisaient un meilleur code que les compilateurs C. Ce n’est pas le lieu de discussions sur les mérites généraux d’une langue par rapport à une autre. Ce qui m'intéresserait vraiment, c’est de savoir si quelqu'un peut trouver un moyen de convaincre gfortran de produire un daxpy aussi efficace que celui en C utilisant la vectorisation explicite, car cela illustre bien le problème du recours exclusif au compilateur pour l'optimisation SIMD, ou encore cas dans lequel un compilateur Fortran surpasse sa contrepartie C.

Pedro
la source
Un problème de synchronisation réside dans le fait que si votre processeur utilise le mode pas à pas fréquence / turbo, ces résultats pourraient être généralisés.
Bill Barth
1
Votre daxpy_c.c met actuellement à jour x avec un multiple de x et ne touche pas y du tout. Vous voudrez peut-être résoudre ce problème pour le rendre juste ...
Jack Poulson
1
@JackPoulson: Bonne prise, correction et mise à jour des résultats.
Pedro
2
De plus, je suis à peu près certain que la différence est entièrement due au déroulement manuel dans la version Fortran, ce qui confond le compilateur. Lorsque je le remplace par la même boucle simple que celle que vous avez mise dans votre version C, les performances entre les deux sont presque identiques. Sans ce changement, la version Fortran était plus lente avec les compilateurs Intel.
Jack Poulson
1
@permeakra: En fait, la norme C99 spécifie le restrictmot clé qui indique exactement au compilateur: supposer qu'un tableau ne chevauche aucune autre structure de données.
Pedro

Réponses:

37

La différence dans vos timings semble être due au déroulement manuel du daxpy Fortran à grandes foulées . Les timings suivants s’appliquent au Xeon X5650 à 2,67 GHz, à l’aide de la commande

./test 1000000 10000

Compilateurs Intel 11.1

Fortran avec déroulement manuel: 8,7 secondes
Fortran sans déroulement manuel: 5,8 secondes
C sans déroulement manuel: 5,8 secondes

Compilateurs GNU 4.1.2

Fortran avec déroulement manuel: 8.3 sec
Fortran sans déroulement manuel: 13.5 sec
C sans déroulement manuel: 13.6 sec
C avec attributs vectoriels: 5.8 sec

Compilateurs GNU 4.4.5

Fortran avec déroulage manuel: 8.1 sec
Fortran sans déroulage manuel: 7.4 sec
C sans déroulage manuel: 8.5 sec
C avec vecteur atrribué: 5.8 sec

Conclusions

  • Le déroulement manuel a aidé les compilateurs Fortran de GNU 4.1.2 sur cette architecture, mais nuit à la nouvelle version (4.4.5) et au compilateur Intel Fortran.
  • Le compilateur GNU 4.4.5 C est beaucoup plus compétitif avec Fortran que pour la version 4.2.1.
  • Les propriétés intrinsèques des vecteurs permettent aux performances du GCC de correspondre aux compilateurs Intel.

Il est temps de tester des routines plus compliquées comme dgemv et dgemm?

Jack Poulson
la source
Merci pour les résultats! Quelle version de gcc utilisiez-vous et pouvez-vous être un peu plus spécifique en ce qui concerne le processeur?
Pedro
2
Votre compilateur est plus ancien que votre processeur ... Pouvez-vous essayer avec gcc-4.5?
Pedro
1
Je viens d'essayer. La version vectorisée avec GCC 4.4.5 correspond exactement aux résultats d'Intel 11.1.
Jack Poulson
1
Je viens d’installer gcc / gfortran version 4.4.5 et je ne peux pas reproduire les différences sans le dérouler. En fait, dans l'assembleur généré pour les deux cas, la boucle la plus à l'intérieur est identique à l'exception des noms de registre utilisés, qui sont interchangeables. Pouvez-vous relancer vos tests juste pour être sûr?
Pedro
4
Pouvons-nous dire que cela règle le vieux débat "nous continuons à utiliser le fortran parce qu'il est plus performant", afin que nous puissions enfin le jeter dans la benne à ordures?
Stefano Borini
16

J'arrive en retard à cette soirée, alors il m'est difficile de suivre le va-et-vient de tout ce qui précède. La question est vaste et je pense que si vous êtes intéressé, elle pourrait être scindée en morceaux plus petits. Une chose qui m'a intéressé était simplement la performance de vos daxpyvariantes, et si Fortran est plus lent que C sur ce code très simple.

Sous mon ordinateur portable (Macbook Pro, Intel Core i7, 2,66 GHz), les performances relatives de votre version C vectorisée à la main et de la version Fortran non vectorisée à la main dépendent du compilateur utilisé (avec vos propres options):

Compiler     Fortran time     C time
GCC 4.6.1    5408.5 ms        5424.0 ms
GCC 4.5.3    7889.2 ms        5532.3 ms
GCC 4.4.6    7735.2 ms        5468.7 ms

Il semble donc que GCC a mieux réussi à vectoriser la boucle dans la branche 4.6 qu'auparavant.


Sur l'ensemble du débat, je pense que l'on peut à peu près écrire du code rapide et optimisé à la fois en C et en Fortran, presque comme dans le langage assembleur. Je ferai cependant remarquer une chose: tout comme un assembleur est plus fastidieux à écrire que C, mais vous donne un contrôle plus fin sur ce qui est exécuté par le CPU, C est plus bas que Fortran. Ainsi, il vous donne plus de contrôle sur les détails, ce qui peut vous aider à optimiser les situations où la syntaxe standard de Fortran (ou ses extensions de fournisseur) peut manquer. Un cas est l'utilisation explicite de types de vecteurs, un autre est la possibilité de spécifier manuellement l'alignement de variables, ce dont Fortran est incapable.

F'x
la source
bienvenue à scicomp! Je conviens que les versions du compilateur sont aussi importantes que la langue dans ce cas. Vouliez-vous dire «au lieu de» dans votre dernière phrase?
Aron Ahmadia
9

La façon dont j'écrirais AXPY en Fortran est légèrement différente. C'est la traduction exacte du calcul.

m_blas.f90

 module blas

   interface axpy
     module procedure saxpy,daxpy
   end interface

 contains

   subroutine daxpy(x,y,a)
     implicit none
     real(8) :: x(:),y(:),a
     y=a*x+y
   end subroutine daxpy

   subroutine saxpy(x,y,a)
     implicit none
     real(4) :: x(:),y(:),a
     y=a*x+y
   end subroutine saxpy

 end module blas

Appelons maintenant la routine ci-dessus dans un programme.

test.f90

 program main

   use blas
   implicit none

   real(4), allocatable :: x(:),y(:)
   real(4) :: a
   integer :: n

   n=1000000000
   allocate(x(n),y(n))
   x=1.0
   y=2.0
   a=5.0
   call axpy(x,y,a)
   deallocate(x,y)

 end program main

Maintenant compilons et exécutons-le ...

login1$ ifort -fast -parallel m_blas.f90 test.f90
ipo: remark #11000: performing multi-file optimizations
ipo: remark #11005: generating object file /tmp/ipo_iforttdqZSA.o

login1$ export OMP_NUM_THREADS=1
login1$ time ./a.out 
real    0 m 4.697 s
user    0 m 1.972 s
sys     0 m 2.548 s

login1$ export OMP_NUM_THREADS=2
login1$ time ./a.out 
real    0 m 2.657 s
user    0 m 2.060 s
sys     0 m 2.744 s

Notez que je n’utilise aucune boucle ou directive OpenMP explicite . Cela serait-il possible en C (c'est-à-dire, pas d'utilisation de boucles et de parallélisation automatique)? Je n'utilise pas C alors je ne sais pas.

Stali
la source
La parallélisation automatique est une caractéristique des compilateurs Intel (Fortran et C), et non du langage. Par conséquent, l'équivalent en C devrait également être parallélisé. Juste par curiosité, comment ça marche pour un n = 10000 plus modéré?
Pedro
3
C'était tout le problème. Autopar est plus facile à Fortran car Fortran (contrairement à C) supporte les opérations sur les tableaux entiers comme matmult, transpose, etc. Ainsi, l'optimisation du code est plus facile pour les compilateurs Fortran. GFortran (que vous avez utilisé) ne dispose pas des ressources de développement nécessaires pour optimiser le compilateur Fortran car son objectif est actuellement de mettre en œuvre le standard Fortran 2003 plutôt que l’optimisation.
Stali
Uhmm ... Le compilateur Intel C / C ++ effectue iccégalement la parallélisation automatique. J'ai ajouté un fichier icctest.caux autres sources. Pouvez-vous le compiler avec les mêmes options que celles que vous avez utilisées ci-dessus, l'exécuter et en indiquer les horaires? J'ai dû ajouter une instruction printf à mon code pour éviter que gcc optimise tout. Ceci est juste un rapide bidouillage et j'espère que c'est sans bug!
Pedro
J'ai téléchargé les derniers compilateurs icc / ifort et effectué les tests moi-même. La question a été mise à jour pour inclure ces nouveaux résultats, à savoir que l'autovectorisation d'Intel fonctionne à la fois en Fortran et en C.
Pedro
1
Merci. Oui, j'ai remarqué qu'il y a peu de différence, peut-être parce que les boucles sont simples et que les opérations sont de niveau 1 BLAS. Mais comme je l'ai dit précédemment, en raison de la capacité de Fortran à effectuer des opérations sur des tableaux entiers et à utiliser des mots clés tels que PURE / ELEMENTAL, l'optimisation du compilateur est plus large. Comment les compilateurs utilisent ces informations et ce qu’elles font vraiment est une chose différente. Vous pouvez également essayer matmul si vous le souhaitez. Bpaste.net/show/23035
stali
6

Je pense que la façon dont un compilateur optimise le code pour du matériel moderne n’est pas seulement intéressante. Surtout entre GNU C et GNU Fortran, la génération de code peut être très différente.

Prenons donc un autre exemple pour montrer les différences entre eux.

En utilisant des nombres complexes, le compilateur GNU C génère une surcharge importante pour une opération arithmétique presque très basique sur un nombre complexe. Le compilateur Fortran donne un code bien meilleur. Jetons un coup d'œil au petit exemple suivant dans Fortran:

COMPLEX*16 A,B,C
C=A*B

donne (gfortran -g -o complex.fo -c complex.f95; objdump -d -S complex.fo):

C=A*B
  52:   dd 45 e0                fldl   -0x20(%ebp)
  55:   dd 45 e8                fldl   -0x18(%ebp)
  58:   dd 45 d0                fldl   -0x30(%ebp)
  5b:   dd 45 d8                fldl   -0x28(%ebp)
  5e:   d9 c3                   fld    %st(3)
  60:   d8 ca                   fmul   %st(2),%st
  62:   d9 c3                   fld    %st(3)
  64:   d8 ca                   fmul   %st(2),%st
  66:   d9 ca                   fxch   %st(2)
  68:   de cd                   fmulp  %st,%st(5)
  6a:   d9 ca                   fxch   %st(2)
  6c:   de cb                   fmulp  %st,%st(3)
  6e:   de e9                   fsubrp %st,%st(1)
  70:   d9 c9                   fxch   %st(1)
  72:   de c2                   faddp  %st,%st(2)
  74:   dd 5d c0                fstpl  -0x40(%ebp)
  77:   dd 5d c8                fstpl  -0x38(%ebp)

Qui sont 39 octets de code machine. Quand on considère la même chose en C

 double complex a,b,c; 
 c=a*b; 

et jetez un oeil à la sortie (faite de la même manière que ci-dessus), on obtient:

  41:   8d 45 b8                lea    -0x48(%ebp),%eax
  44:   dd 5c 24 1c             fstpl  0x1c(%esp)
  48:   dd 5c 24 14             fstpl  0x14(%esp)
  4c:   dd 5c 24 0c             fstpl  0xc(%esp)
  50:   dd 5c 24 04             fstpl  0x4(%esp)
  54:   89 04 24                mov    %eax,(%esp)
  57:   e8 fc ff ff ff          call   58 <main+0x58>
  5c:   83 ec 04                sub    $0x4,%esp
  5f:   dd 45 b8                fldl   -0x48(%ebp)
  62:   dd 5d c8                fstpl  -0x38(%ebp)
  65:   dd 45 c0                fldl   -0x40(%ebp)
  68:   dd 5d d0                fstpl  -0x30(%ebp)

Ce qui sont également du code machine de 39 octets, mais auquel l’étape 57 fait référence, effectue la partie correcte du travail et effectue l’opération souhaitée. Nous avons donc un code machine à 27 octets pour exécuter la multi-opération. La fonction derrière est muldc3 fournie par libgcc_s.soet a une empreinte de 1375 octets dans le code machine. Cela ralentit considérablement le code et donne un résultat intéressant lors de l'utilisation d'un profileur.

Lorsque nous implémentons les exemples BLAS ci-dessus pour zaxpyle même test et que nous le réalisons , le compilateur Fortran devrait donner de meilleurs résultats que le compilateur C.

(J’ai utilisé GCC 4.4.3 pour cette expérience, mais j’ai remarqué ce comportement dans lequel un autre GCC est relâché.)

Donc, à mon avis, non seulement nous pensons à la parallélisation et à la vectorisation, mais nous nous devons également de voir comment les éléments de base sont traduits en code assembleur. Si cette traduction donne un code incorrect, l'optimisation ne peut utiliser ces informations qu'en entrée.

MK aka Grisu
la source
1
Je viens de préparer un exemple inspiré de votre code complex.cet de l'ajouter au code en ligne. J'ai dû ajouter toutes les entrées / sorties pour m'assurer que rien n'est optimisé. Je ne reçois un appel que __muldc3si je ne l'utilise pas -ffast-math. Avec -O2 -ffast-mathje reçois 9 lignes d'assembleur en ligne. Pouvez-vous confirmer cela?
Pedro
J'ai trouvé une cause plus spécifique de la différence dans l'assembleur généré et je l'ai ajoutée à ma question ci-dessus.
Pedro
En utilisant -O2, le compilateur calcule tout ce qui est possible au moment de l'exécution, c'est pourquoi de telles constructions sont parfois perdues. L'option -fast-math ne doit pas être utilisée en calcul scientifique lorsque vous souhaitez vous appuyer sur les sorties.
MK aka Grisu
1
Eh bien, par cet argument (non -ffast-math), vous ne devriez pas utiliser Fortran pour vos calculs à valeurs complexes. Comme je le décris dans la mise à jour de ma question, -ffast-mathou plus généralement, -fcx-limited-rangeforce gcc à utiliser les mêmes calculs de plage restreinte non IEEE que ceux standard dans Fortran. Donc, si vous voulez toute la gamme des valeurs complexes et des Infs et NaN corrects, vous ne devriez pas utiliser Fortran ...
Pedro
2
@ Pedro: Si vous voulez que GCC se comporte comme GFortran multiplication et division complexes, vous devez utiliser les règles -fcx-fortran.
janneb
4

Gens,

J'ai trouvé cette discussion très intéressante, mais j'ai été surpris de voir que le fait de réorganiser les boucles dans l'exemple de Matmul a changé la donne. Je n'ai pas de compilateur intel disponible sur ma machine actuelle, donc j'utilise gfortran, mais une réécriture des boucles dans le mm_test.f90 à

call cpu_time(start)  
do r=1,runs  
  mat_c=0.0d0  
     do j=1,n  
        do k=1,n  
  do i=1,n  
           mat_c(i,j)=mat_c(i,j)+mat_a(i,k)*mat_b(k,j)  
        end do  
     end do  
  end do  
end do  
call cpu_time(finish)  

changé tous les résultats pour ma machine.

Les résultats de la version précédente étaient les suivants:

#time ./mm_test_f 10000 100
 matmul    time   6.3620000000000001     
 triple do time   21.420999999999999     

alors qu'avec les triples boucles réorganisées comme ci-dessus:

#time ./mm_test_f 10000 100
 matmul    time   6.3929999999999998     
 triple do time   3.9190000000000005    

Il s’agit de gcc / gfortran 4.7.2 20121109 sur un processeur Intel (R) Core (TM) i7-2600K à 3,40 GHz.

Les drapeaux de compilateur utilisés étaient ceux du Makefile que j'ai obtenu ici ...

Schatzi
la source
3
Ce n’est pas surprenant, car le stockage matriciel en mémoire favorise un ordre, c’est-à-dire que si les rangées sont stockées de manière contiguë, il est préférable de boucler sur les rangées les plus proches, car vous pouvez alors charger chaque rangée une fois dans la mémoire locale rapide par rapport au chargement répété (une tranche de ) pour accéder à un seul élément. Voir stackoverflow.com/questions/7395556 .
Christian Clason
Je suppose que j'ai été surpris que le "matmul intrinsèque" ne soit pas codé pour faire les choses de cette façon. C'est beaucoup plus rapide avec le triple do commandé de la deuxième manière. Cela semble faire partie de cet ensemble de compilateur, car les versions précédentes de gfortran auxquelles je pouvais accéder étaient plus "plates" dans leur timing - peu importait la façon dont vous réalisiez le mult - cela prenait presque le même temps.
Schatzi
-2

Ce ne sont pas les langues qui accélèrent l’exécution du code, bien qu’elles aident. C'est le compilateur, le processeur et le système d'exploitation qui accélèrent l'exécution des codes. Comparer des langues n'est qu'un abus de langage, inutile et dénué de sens. Cela n'a aucun sens, car vous comparez deux variables: le langage et le compilateur. Si un code s'exécute plus rapidement, vous ne savez pas à quel point c'est le langage ou le compilateur. Je ne comprends pas pourquoi la communauté informatique ne comprend tout simplement pas ceci :-(

Wadud Miah
la source