为什么我无法通过缓存友好来加速我的程序?

Why did I failed to speed up my program by making it cache-friendly?

我使用了intel的ifort来编译我的程序

有很多像下面这样的嵌套循环

do i=1,imax
do j=1,jmax
if (a(i,j).ne.1) cycle
.....
.....
.....
enddo
enddo 

以及像

这样的一些循环
do j=1,jmax
do i=1,imax
if (a(i,j).ne.1) cycle
.....
.....
.....
enddo
enddo 

我注意到 Fortran 按列优先顺序存储数组,所以我将 j 循环放在 i 循环之外。

让我感到困惑的是,性能似乎有所下降。

相比之下,我试图将第 i 个循环放在第 j 个循环的外部,用于其他循环。但是仍然存在性能损失,但是轻微


program p 
integer ::i, j, k, s
real ::a(1000,1000)

do i=1,1000
  do j=1,1000
    call random_number(a(i,j))
  enddo
enddo

do k=1,10000
  do j=1,1000
    do i=1,1000
      if (a(i,j) .ge. 111) s = s+a(i,j)
    enddo
  enddo
enddo

print *,s

end

ifort -fPIC -no-prec-div -O3 -xCORE-AVX2 a.f90 -o a 时间./a 10921820

real    0m2.221s
user    0m2.221s
sys     0m0.001s

program p 
integer ::i, j, k, s
real ::a(1000,1000)

do i=1,1000
  do j=1,1000
    call random_number(a(i,j))
  enddo
enddo

do k=1,10000
  do i=1,1000
    do j=1,1000
      if (a(i,j) .ge. 111) s = s+a(i,j)
    enddo
  enddo
enddo

print *,s

end

ifort -fPIC -no-prec-div -O3 -xCORE-AVX2 a.f90 -o a

time ./a
    10923324

real    0m4.459s
user    0m4.457s
sys     0m0.003s

性能差异稳定,可以证明优化。但是当我把它应用到我的实际项目中时,我失败了。

再次感谢您,我对堆栈溢出还很陌生,感谢您的帮助

正如您正确提到的那样,Fortran 是专栏专业的。这意味着该列是主要索引,并且矩阵的所有列都一个接一个地存储在内存中。您可能假设,列优先意味着遍历列索引意味着遍历内存中连续的元素的矩阵,但事实并非如此。矩阵

| a11, a12, a13 |
| a21, a22, a23 |
| a31, a32, a33 |

按列优先顺序存储为

a11, a21, a31, a12, a22, a32, a13, a23, a33
--column 1---  --column 2---  --column 3---

这意味着当你在 Fortran 中创建一个二维循环时,第一个索引,代表行索引,应该总是在最里面以获得最佳性能,因为这样你就可以按内存顺序遍历矩阵我在上面写的。因此

do j=1,1000
    do i=1,1000
        if (a(i,j) .ge. 111) s = s + a(i,j)
    enddo
enddo

如您所见,性能最佳。