gpt4 book ai didi

loops - 为什么这些 Fortran 95 循环方法的执行时间不同?

转载 作者:行者123 更新时间:2023-12-01 10:29:16 25 4
gpt4 key购买 nike

我有一个用 Fortran 语言做矩阵运算的示例程序,它有列主系统来存储矩阵。这是否会导致两个数组操作在运行时出现如此显着的差异?如果是这样,有人可以解释为什么会发生这种情况以及究竟是什么导致了如此大的运行时差异?

我正在使用 Ubuntu 14.04 和 GNU Fortran 4.8.4。

代码:

program main
implicit none

integer :: i,j
real :: start, finish
real,dimension(256,256) :: arr1

!ROW format - put 0 to main diagonal
call cpu_time(start)
do i=1,255,1
do j=1,255,1
arr1(i,j)=0
end do
end do
call cpu_time(finish)

write(*,100) 'Execution time arr(i,j) in seconds= ', (finish-start)
100 format (A,f12.9)

!COLUMN format - put 1 to main diagonal
call cpu_time(start)
do j=1,255,1
do i=1,255,1
arr1(i,j)=1
end do
end do
call cpu_time(finish)

write(*,100) 'Execution time arr(j,i) in seconds = ', (finish-start)

end program

编译:

gfortran main.f95 -o main 

输出:

Execution time arr(i,j) in seconds=  0.000736000
Execution time arr(j,i) in seconds = 0.000164000

与第二种方法相比,第一种方法大约需要 4.5 倍的执行时间。

编辑:我更感兴趣的是知道为什么执行时间有如此大的差异(当我们进行行主要排序等时,编译器或处理器或内存级别发生了一些奇怪的事情)而不是简单地放置 -o3 标记或优化代码。这个问题optimization of a seven do cycle有一个答案说列主要排序更好。为什么这样?

最佳答案

首先,您的测试存在严重偏差:要查看偏差,请颠倒您正在测试的两个 block 的顺序,事情将开始发生变化。对于这样的测试,您必须:

  1. 编写两个不同的程序,每个程序一个;
  2. 多次运行每个程序并取平均时间;

您也可以根据自己的兴趣,选择将第二步替换为循环。

现在,回到您的问题,我首先要提到这个问题太宽泛了,就像 francescalus 提到的那样。简而言之;计算机内存被组织成一个层次结构:

  1. 内存;
  2. 缓存(可以有很多层,为简单起见我们考虑一层);
  3. 注册

任何级别都有其优点和缺点:

  1. RAM 可以很大(千兆字节),但速度很慢(大约几十纳秒,50 到 70)。
  2. 缓存不是很大(几千到几兆字节),比 RAM 快(几纳秒 0.5 到 2.5)
  3. 寄存器不大(只有几十字节),速度很快。

参见示例 this link了解更多信息。我忽略了作为另一层内存和网络的磁盘。

数据通常只从一层内存传输到下一层:即从 RAM 到缓存,从缓存到 RAM,从缓存到寄存器,从寄存器到缓存。 CPU 仅在访问速度更快的寄存器上运行。所以对于每一个操作,数据都是从RAM中取到寄存器中,在计算之后,它们又被带回RAM中。哦不,没那么快。让我们保持简单,假设 CPU 在字节上运行(如果你更深入,你将了解单词的概念是一组连续的字节和页面的概念是一组连续的单词)。

当你访问一个不在缓存中的字节时,就会出现缓存错误,该字节先从 RAM 进入缓存,然后再进入寄存器进行你的操作。当系统将该字节从 RAM 取到缓存时,它会将一组连续的字节放在一起。这样如果下一个操作是在最近的邻居上进行的,就不需要去 RAM。

现在在您的程序中发生的事情是,fortran 按列存储数组,这意味着在内存中元素按以下顺序存储:

a(1,1) a(2,1) a(3,1) ... a(M,1) a(1,2) a(2,2) a(3,2) ... a(M,2) ...

所以循环

do j=1,255,1
do i=1,255,1
arr1(i,j)=1
end do
end do

是按照元素存储在内存中的顺序访问元素。 RAM 和缓存之间的往返次数减少到最少。

对于另一个循环

do i=1,255,1
do j=1,255,1
arr1(i,j)=1
end do
end do

您根本没有以正确的顺序访问元素。例如,如果您的缓存只能容纳少于矩阵的一列,则意味着对于内部循环的任何迭代,系统都必须重新填充缓存。而不是那么简单,要重新填充缓存,如果缓存中的数据被修改,系统会先将缓存中的数据复制回RAM,这里就是这样。要看到这一点,将矩阵增加到您的 RAM 可以处理的最大大小,您将看到不遵循存储逻辑意味着什么,差距会增加。你可以逐渐增加,1000x1000,然后 10000x10000,等等。当你的缓存只能容纳一个或更少的列时,你将得到一个接近 RAM 和缓存访问时间之间的因子。记住,超过 10 个。

内存是许多计算机科学类(class)的主题。我只想给你我能很快给的东西。

关于loops - 为什么这些 Fortran 95 循环方法的执行时间不同?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31898307/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com