为啥我无法通过使其对缓存友好来加速我的程序?

Posted

技术标签:

【中文标题】为啥我无法通过使其对缓存友好来加速我的程序?【英文标题】:Why did I failed to speed up my program by making it cache-friendly?为什么我无法通过使其对缓存友好来加速我的程序? 【发布时间】:2016-04-19 06:16:28 【问题描述】:

我使用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

性能差异是稳定的,这可能证明了优化。但是,当我将它改编为我的实际项目时,我失败了。

再次感谢您,我是堆栈溢出的新手,感谢您的帮助

【问题讨论】:

很难说没有看到任何代码。最好是创建一个最小的工作示例...... 谢谢你。我刚刚完成了那个最小的工作示例(类似的循环结构,相同的编译选项)。事实证明,性能应该一直都有提升,而我的程序却没有。 这不是一个最小的例子,一个最小的例子必须是completeverifiable。 ***.com/help/mcve 谢谢,我更新了我的问题。我应该提供更多细节吗? 我不明白你的问题,结果完全符合预期。 k,j,i 订单应该更快。顺便说一句,a(i,j) 怎么会变成>= 111?答案怎么可能不是 0?我认为这只是因为您没有在开头设置s=0 【参考方案1】:

正如您正确提到的,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

如您所见,提供最佳性能。

【讨论】:

我一直觉得这些行和列非常混乱。我实际上通过记住 Fortran 应该是“主要列”而不是相反来记住哪个索引应该是行和列。但是,我也确实相信这些列是连续的,你为什么说它们不是? 根据矩阵表示法,第一个索引是行,第二个索引是列。在此示例中,a(1,1)a(2,1) 表示我放置的示例矩阵中的元素 a11a21。这两个元素在内存中是连续的,因为列(以矩阵表示法)是主要坐标。您可以颠倒推理并说您希望列在内存中是连续的,并且您使用另一种矩阵表示法,其中第一个索引是列,第二个索引是行。 我听不懂您的话,但仅通过查看您的矩阵图片及其下方的元素,我发现您的示例中的列是连续的... 顺便说一句,我们在 Les Houches 见过吗? 我遵循这个约定:en.wikipedia.org/wiki/Matrix_(mathematics)。行是矩阵中的一条水平线,由第一个索引表示,一列是垂直线,由第二个索引表示。列作为一个整体确实是连续的......术语仍然令人困惑。我想我们确实在那里见过:)

以上是关于为啥我无法通过使其对缓存友好来加速我的程序?的主要内容,如果未能解决你的问题,请参考以下文章

应用程序的缓存友好设计

如何更改字符串以使其对 URL 友好? [复制]

使内存密集型后台应用程序“友好”

为啥我的微信无法打开小程序了呢?

我只像管理员一样看到“我的订单”列表,如何使其对所有客户可见?

如何简单地使其对公众保密?无需为我的脚本更改任何内容