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