假设我有一个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,编译器是numpy
和sum
)在我有两台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
似乎具有相当的可比性,所以我想这取决于个人喜好哪个更喜欢。
答案 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)
但可能需要对重塑进行重新排序以使减少超过所需的尺寸。这很容易变得难以理解或不清楚。