Fortran multiplication de matrice de performance dans les différentes optimisation

Je suis en train de lire le livre "Scientifique de Développement de Logiciels avec Fortran", et il y a un exercice à elle que je pense très intéressant:

"Créer un module Fortran appelé MatrixMultiplyModule. Ajouter trois sous-programmes à elle appelé LoopMatrixMultiply, IntrinsicMatrixMultiply, et MixMatrixMultiply. Chaque routine devrait prendre deux matrices réelles comme argument, effectuer une multiplication de matrice, et de retourner le résultat par un troisième argument. LoopMatrixMultiply doit être écrit entièrement à faire des boucles, et non les opérations de matrice ou intrinsèques des procédures; IntrinsicMatrixMultiply doit être écrit en utilisant le matmul fonction intrinsèque; et MixMatrixMultiply doit être écrite à l'aide de certains faire des boucles et de la fonction intrinsèque dot_product. Écrire un petit programme pour tester les performances de ces trois façons différentes d'effectuer la multiplication de matrice pour différentes tailles de matrices."

J'ai fait quelques test de multiplier de deux rang 2 de la matrice et voici les résultats, en vertu de différentes options d'optimisation:

Fortran multiplication de matrice de performance dans les différentes optimisation
Fortran multiplication de matrice de performance dans les différentes optimisation
Fortran multiplication de matrice de performance dans les différentes optimisation

compiler:ifort version 13.0.0 on Mac 

Voici ma question:

Pourquoi sous -O0 ils ont à peu près la même performance mais matmul a énorme gain de performance lors de l'utilisation d'-O3, tout explicite de la boucle et le produit scalaire a moins de performances? Aussi, pourquoi dot_product semble avoir les mêmes performances comparer explicite de faire des boucles?

Le code que j'utilise est la suivante:

module MatrixMultiplyModule

contains
    subroutine LoopMatrixMultiply(mtx1,mtx2,mtx3)
        real,intent(in)                 ::  mtx1(:,:),mtx2(:,:)
        real,intent(out),allocatable    ::  mtx3(:,:)
        integer                         ::  m,n
        integer                         ::  i,j
        if(size(mtx1,dim=2) /= size(mtx2,dim=1)) stop "input array size not match"
        m=size(mtx1,dim=1)
        n=size(mtx2,dim=2)
        allocate(mtx3(m,n))
        mtx3=0.

        do i=1,m
            do j=1,n
                do k=1,size(mtx1,dim=2)
                    mtx3(i,j)=mtx3(i,j)+mtx1(i,k)*mtx2(k,j)
                end do      
            end do
        end do
    end subroutine

    subroutine IntrinsicMatrixMultiply(mtx1,mtx2,mtx3)
        real,intent(in)                 ::  mtx1(:,:),mtx2(:,:)
        real,intent(out),allocatable    ::  mtx3(:,:)
        integer                         ::  m,n
        integer                         ::  i,j
        if(size(mtx1,dim=2) /= size(mtx2,dim=1)) stop "input array size not match"
        m=size(mtx1,dim=1)
        n=size(mtx2,dim=2)
        allocate(mtx3(m,n))
        mtx3=matmul(mtx1,mtx2)

    end subroutine

    subroutine MixMatrixMultiply(mtx1,mtx2,mtx3)
        real,intent(in)                 ::  mtx1(:,:),mtx2(:,:)
        real,intent(out),allocatable    ::  mtx3(:,:)
        integer                         ::  m,n
        integer                         ::  i,j
        if(size(mtx1,dim=2) /= size(mtx2,dim=1)) stop "input array size not match"
        m=size(mtx1,dim=1)
        n=size(mtx2,dim=2)
        allocate(mtx3(m,n))

        do i=1,m
            do j=1,n
                    mtx3(i,j)=dot_product(mtx1(i,:),mtx2(:,j))
            end do
        end do

    end subroutine

end module




program main
use MatrixMultiplyModule
implicit none

real,allocatable        ::  a(:,:),b(:,:)
real,allocatable    ::  c1(:,:),c2(:,:),c3(:,:)
integer ::  n
integer ::  count, rate
real    ::  timeAtStart, timeAtEnd
real    ::  time(3,10)
do n=100,1000,100
    allocate(a(n,n),b(n,n))

    call random_number(a)
    call random_number(b)

    call system_clock(count = count, count_rate = rate)
    timeAtStart = count /real(rate)
    call LoopMatrixMultiply(a,b,c1)
    call system_clock(count = count, count_rate = rate)
    timeAtEnd = count /real(rate)
    time(1,n/100)=timeAtEnd-timeAtStart

    call system_clock(count = count, count_rate = rate)
    timeAtStart = count /real(rate)
    call IntrinsicMatrixMultiply(a,b,c2)
    call system_clock(count = count, count_rate = rate)
    timeAtEnd = count /real(rate)
    time(2,n/100)=timeAtEnd-timeAtStart

    call system_clock(count = count, count_rate = rate)
    timeAtStart = count /real(rate)
    call MixMatrixMultiply(a,b,c3)
    call system_clock(count = count, count_rate = rate)
    timeAtEnd = count /real(rate)
    time(3,n/100)=timeAtEnd-timeAtStart


    deallocate(a,b)

end do

open(1,file="time.txt")
do n=1,10
    write(1,*) time(:,n)
end do
close(1)
deallocate(c1,c2,c3)
end program
Votre programme n'a pas fait d'utiliser les résultats de la multiplication de matrice. Peut-être pas applicable ici, mais sachez que si le compilateur est assez intelligent il sera tout simplement éliminer l'appel à divers matrice routines entièrement. Quelles sont les autres options de ligne de commande utilisez-vous?
Pas d'autres indicateurs ont été utilisés. Je n'utilise que le drapeau -O dans ces cas, par exemple, ifort -O3 sub.f90 main.f90.
Vous pouvez vous assurer que le code est utilisé par l'impression, le vecteur résultant d'un fichier de travail à l'extérieur de l'échéancier des appels.
L'une des raisons de ce genre d'exercice est inclus dans ce genre de livre est pour vous provoquer dans la pensée, et de l'apprentissage, de plus en plus profondément au sujet de la performance du programme: des sujets tels que la façon dont vous structurez votre code dans un langage de haut niveau, comment le compilateur traduit en assembleur, ce que les différentes transformations que le compilateur fait pour les différents niveaux de l'optimisation, et bien plus encore. Alors, qu'en pensez-vous ? Cache, vecteur des instructions, des modèles d'accès mémoire, boucle de dérouler, le tableau de carrelage, de la rigueur de la f-p opérations, toutes sortes de facteurs entrent en jeu et sont mérite d'être étudié.
mon test montre que pour matmul -O3 est beaucoup plus rapide que -O2. Pour 1000x1000 aléatoire réelle de la multiplication de matrice. -O3 prend 0.14 s, -O2 prend 0.48 s

OriginalL'auteur xslittlegrass | 2013-03-22