我有两(1d)个数组,一个长的A
(大小为m)和一个较短的B
(大小为n)。我想通过在特定索引处添加短数组的每个元素来更新长数组。
从原理上讲,数组的结构如下:
A = [a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 a12 a13 a14 ... am]
B = [ b1 b2 b3 b4 b5 b6 b7 b8 b9 ... bn ]
我想通过添加A
的相应元素来更新B
。
最直接的方法是拥有一些索引数组indarray
(大小与B
相同),它告诉我们A
的哪个索引对应于B(i)
:
do i = 1, size(B)
A(indarray(i)) = A(indarray(i)) + B(i)
end do
但是,我认为有一个组织可以解决这个问题,
以矢量方式进行此操作应该没有障碍。即每个i
的更新都是独立的,可以按任何顺序进行。
无需在数组A
中来回跳转。机器应该知道只遍历数组一次,仅在必要时更新A
。
应该不需要任何临时数组。
在Fortran中执行此操作的最佳方法是什么?
一种方法可能是使用PACK
,UNPACK
和布尔型掩码M
(与A
相同),其作用与indarray
相同:
A = [a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 a12 a13 a14 ... am]
B = [ b1 b2 b3 b4 b5 b6 b7 b8 b9 ... bn ]
M = [. T T T . T T . . T . T T T T . ]
(其中T
代表.true.
,.
是.false.
)。
代码就是
A = UNPACK(PACK(A, M) + B, M, A)
这非常简洁,可能满足(1)和(2)的要求(似乎在整个数组中执行了两个循环,而不仅仅是一个循环)。但是我担心机器会在此过程中创建一些临时数组,这似乎是不必要的。
将where
与UNPACK
一起使用怎么办?
where (M)
A =A + UNPACK(B, M, 0.0d0)
end where
这似乎与选项2相同(两个循环,可能会创建临时数组)。它还必须用M=.false.
填充UNPACK数组中的0
元素,这似乎是一种浪费。
在我的情况下,遮罩的.true.
元素通常位于连续的块中(即,连续几个true,然后是一堆false,然后是另一个true,等等)。也许这可能会导致类似于选项1的情况。假设有K
个块中的.true.
个。我需要一个数组indstart
(大小为K
),该索引为每个真实块的开始处的A
提供一个索引,以及一个索引blocksize
(大小为{{1} })加上真正区块的长度。
K
至少这只会循环一次。这段代码似乎更明确地说明了数组中没有来回跳跃的事实。但是我认为编译器无法弄清楚(例如j = 1
do i = 1, size(indstart)
i0 = indstart(i)
i1 = i0 + blocksize(i) - 1
A(i0:i1) = A(i0:i1) + B(j:j+blocksize(i)-1)
j = j + blocksize(i)
end do
可能具有负值)。因此,此选项可能不会导致矢量化结果。
-
有什么想法可以做到这一点吗?在我的情况下,数组blocksize
,indarray
,M
和indstart
只能创建一次,但必须对不同的数组blocksize
进行多次添加操作和A
(尽管这些数组将具有恒定的大小)。 B
语句似乎很重要。