Wydajność mnożenia macierzy Fortrana przy różnych optymalizacjach

Czytam książkę „Naukowe tworzenie oprogramowania z Fortranem” i jest w niej ćwiczenie, które uważam za bardzo interesujące:

„Utwórz moduł Fortran o nazwie MatrixMultiplyModule. Dodaj do niego trzy podprogramy o nazwie LoopMatrixMultiply, IntrinsicMatrixMultiply i MixMatrixMultiply. Każda procedura powinna przyjąć dwie rzeczywiste macierze jako argument, wykonać mnożenie macierzy i zwrócić wynik za pomocą trzeciego argumentu. LoopMatrixMultiply powinien być zapisany w całości z pętlami do i bez operacji tablicowych lub procedur wewnętrznych, IntrinsicMatrixMultiply powinien być napisany z wykorzystaniem wewnętrznej funkcji matmul, a MixMatrixMultiply powinien być napisany przy użyciu kilku pętli do zrobienia i wewnętrznej funkcji dot_product Napisz mały program, aby przetestować działanie tych trzech różnych sposobów wykonywania mnożenia macierzy dla różnych rozmiarów matryc. ”

Zrobiłem test mnożenia dwóch macierzy rangi 2 i oto wyniki pod różnymi flagami optymalizacji:

compiler:ifort version 13.0.0 on Mac 

Oto moje pytanie:

Dlaczego pod -O0 mają mniej więcej taką samą wydajność, ale matmul ma ogromny wzrost wydajności przy użyciu -O3, podczas gdy wyraźny produkt pętli i kropki ma mniejszy wzrost wydajności? Ponadto dlaczego dot_product wydaje się mieć taką samą wydajność w porównaniu z jawnymi pętlami do wykonywania?

Kod, którego używam, jest następujący:

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

questionAnswers(1)

yourAnswerToTheQuestion