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

时间:2015-08-08 20:35:22

标签: loops fortran fortran95

我有一个示例程序在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有一个答案,即列主要排序更好。为什么这样?

2 个答案:

答案 0 :(得分:3)

首先,您的测试存在很大偏差: 要查看偏差,反转您正在测试的两个块的顺序,事情将开始改变。对于这样的测试,你必须:

  1. 写两个不同的程序,每个案例一个;
  2. 多次运行每个程序并取平均时间;
  3. 您还可以选择循环替换第二步,具体取决于您感兴趣的内容。

    现在,回到你的担忧,我将首先提到这个问题太过宽泛,正如francescalus所提到的那样。把故事缩短;计算机内存按层次结构组织:

    1. RAM;
    2. 缓存(可以有多个级别,为简单起见,我们考虑一个级别);
    3. 寄存器
    4. 任何级别都有其优点和缺点:

      1. RAM可能很大(千兆字节),但速度很慢(大约几十纳秒,50到70)。
      2. 缓存不是很大(几千到几兆字节),比RAM快(几纳秒0.5到2.5)
      3. 寄存器不大(只有几十个字节),非常快。
      4. 有关详细信息,请参阅示例this link。我忽略了另一级内存和网络的磁盘。

        数据通常只从一级内存传输到下一级:意味着从RAM到Cache,从缓存到RAM,从Cache到寄存器,从寄存器到缓存。 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和cahe的访问时间之间的因素。记住,超过10个。

        记忆主题是许多计算机科学课程的主题。我想只给你快点给我的东西。

答案 1 :(得分:1)

要处理数据,CPU需要将其从RAM读入其缓存。在单个字节中读取的时间与在相当多的连续字节中读取所需的时间相同。

如果您的iner循环超过非连续维度,则CPU必须独立地从RAM读取和写入每个单值。如果你的内部循环超过了连续的维度,它可以一次读取很多值,然后在它的缓存中对它们进行操作。