最简洁的方法是在m维上求和n维数组

时间:2018-01-25 11:23:31

标签: arrays fortran

假设我有一个n - 维数组,并希望对其维度的m求和,以便最后我留下一个n-m - 维数组({{ 1}})。如果我只是想在一个维度上对一个数组求和,我可以将scalar integer传递给n>m - 内在的,例如SUM。但是,如果我想总结多个维度(但不是全部),我没有找到任何信息如何以简单的方式做到这一点,所以我唯一想到的就是链接几个SUM(array, DIM = 2)命令。举个例子,让我们假设我想要总结3d数组的两个维度:

SUM

这给出了预期的结果。但是,如果我有更多尺寸,这可能会变得很难阅读。那么如何实现这一目标有“更好”的方法吗?例如,在PROGRAM sum_test IMPLICIT NONE INTEGER :: a(2,2,2) = reshape( (/1, 2, 3, 4, 5, 6, 7, 8/), (/2,2,2/), order = (/ 1, 2, 3 /) ) INTEGER :: b(2,2) INTEGER :: c(2) INTEGER :: d(2) d = SUM(SUM(a, DIM=3), DIM=2) WRITE (*,*) d END PROGRAM sum_test 的{​​{1}}中,可以将一个整数序列传递给python命令。

修改

为了测试@francescalus建议的不同解决方案的性能,我编写了一个小程序,并在我的笔记本电脑上使用两个不同的编译器进行编译(带有High Sierra的mac,编译器是numpysum)在我有两台Linux机器上。对于每个编译器,我使用g95作为优化选项。

由于gfortran-mp-7不支持-O2,我将该版本删除了。我也感兴趣的是,总结的排名组合如何影响性能,我在第二和第四级总结了一次4d阵列,在第三和第四级总结了一次。这是代码:

g95

结果在很多方面令人惊讶。对于 mac g95 ,我得到:

do concurrent

对于 mac gfortran-mp-7 我得到了

PROGRAM sum_performance
  IMPLICIT NONE

  INTEGER, PARAMETER :: size = 100
  REAL :: array(size, size, size, size)
  REAL, DIMENSION(size, size) :: res11, res12, res13, res14
  REAL, DIMENSION(size, size) :: res21, res22, res23, res24

  INTEGER :: i,j,k,l
  INTEGER :: clock_start, clock_stop, clock_rate
  INTEGER :: count
  INTEGER, PARAMETER :: repeat = 100

  !getting clock rate:
  CALL SYSTEM_CLOCK(count_rate = clock_rate)

  !setting up array:
  CALL RANDOM_NUMBER(array)

  !summing second and fourth index
  WRITE (*,*) 'second and fourth rank'

  CALL SYSTEM_CLOCK(count = clock_start)
  DO count = 1,repeat
     res11 = SUM(SUM(array, DIM = 4), DIM = 2)
  END DO
  CALL SYSTEM_CLOCK(count = clock_stop)
  WRITE(*,*) 'chained sum:  ', (REAL(clock_stop - clock_start)/&
       REAL(clock_rate))/REAL(repeat)

  CALL SYSTEM_CLOCK(count = clock_start)
  DO count = 1,repeat
     DO l = 1,size
        DO j = 1,size
           res12(j,l) = SUM(array(:,j,:,l))
        END DO
     END DO
  END DO
  CALL SYSTEM_CLOCK(count = clock_stop)
  WRITE(*,*) 'nested do:    ', (REAL(clock_stop - clock_start)/&
       REAL(clock_rate))/REAL(repeat)

  CALL SYSTEM_CLOCK(count = clock_start)
  DO count = 1,repeat
     FORALL (j = 1:size, l = 1:size)
           res13(j,l) = SUM(array(:,j,:,l))
     END FORALL
  END DO
  CALL SYSTEM_CLOCK(count = clock_stop)
  WRITE(*,*) 'forall:       ', (REAL(clock_stop - clock_start)/&
       REAL(clock_rate))/REAL(repeat)


 !summing second and fourth index
  WRITE (*,*)
  WRITE (*,*) 'third and fourth rank'

  CALL SYSTEM_CLOCK(count = clock_start)
  DO count = 1,repeat
     res21 = SUM(SUM(array, DIM = 4), DIM = 3)
  END DO
  CALL SYSTEM_CLOCK(count = clock_stop)
  WRITE(*,*) 'chained sum:  ', (REAL(clock_stop - clock_start)/&
       REAL(clock_rate))/REAL(repeat)

  CALL SYSTEM_CLOCK(count = clock_start)
  DO count = 1,repeat
     DO l = 1,size
        DO k = 1,size
           res22(k,l) = SUM(array(:,:,k,l))
        END DO
     END DO
  END DO
  CALL SYSTEM_CLOCK(count = clock_stop)
  WRITE(*,*) 'nested do:    ', (REAL(clock_stop - clock_start)/&
       REAL(clock_rate))/REAL(repeat)

  CALL SYSTEM_CLOCK(count = clock_start)
  DO count = 1,repeat
     FORALL (k = 1:size, l = 1:size)
           res23(k,l) = SUM(array(:,:,k,l))
     END FORALL
  END DO
  CALL SYSTEM_CLOCK(count = clock_stop)
  WRITE(*,*) 'forall:       ', (REAL(clock_stop - clock_start)/&
       REAL(clock_rate))/REAL(repeat)

END PROGRAM

对于两台linux机器,相对性能与mac g95结果相似,尽管绝对性能不同。可以看出,链式second and fourth rank chained sum: 0.193214 nested do: 0.140472 forall: 0.18884899 third and fourth rank chained sum: 0.196938 nested do: 0.114286005 forall: 0.115414 解决方案显示了最差的性能(可能会因为临时数组创建而导致时间损失?)。此外,在对第3和第4级进行求和时会发生一些有趣的事情(这是一个强大的功能,我检查过几次)。我猜这与临时数组的创建方式有关(如果它们是创建的)。有趣的是它只发生在两个编译器中的一个。嵌套的second and fourth rank chained sum: 0.279830009 nested do: 0.131999999 forall: 0.130150005 third and fourth rank chained sum: 3.01672006 nested do: 0.111460000 forall: 0.110610001 循环和SUM似乎具有相当的可比性,所以我想这取决于个人喜好哪个更喜欢。

1 个答案:

答案 0 :(得分:1)

我不会低估这种情况下可读循环结构的可读性。

很难明智地给出一般食谱,所以我只是举例说明。

对于问题的例子:

do i=1,SIZE(d)
  d(i) = SUM(a(i,:,:))
end do

如果排名结果较高,可以嵌套此类结构,或使用forall结构或do concurrent

forall (i=1:SIZE(d,1), j=1:SIZE(d,2))
  d(i,j) = SUM(a(i,:,:,j)
end

do concurrent (i=1:SIZE(d,1), j=1:SIZE(d,2))
  d(i,j) = SUM(a(i,:,:,j)
end

如果确实需要,可以使用reshape来避免循环/错误:

d = SUM(RESHAPE(a,[2,4]),dim=2)

但可能需要对重塑进行重新排序以使减少超过所需的尺寸。这很容易变得难以理解或不清楚。