首页 > 解决方案 > 在 Fortran 中的特定索引处将数组添加到更长数组的巧妙方法?

问题描述

我有两个(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)

选项1

do i = 1, size(B)
    A(indarray(i)) = A(indarray(i)) + B(i)
end do

但是,有一个组织可以解决这个问题,我觉得应该允许一些更好的性能:

  1. 以矢量化方式执行此操作应该没有障碍。即每个更新i都是独立的,可以按任何顺序进行。

  2. 无需在数组中来回跳转A。机器应该知道只在数组中循环一次,只A在必要时更新。

  3. 应该不需要任何临时数组。

在 Fortran 中执行此操作的最佳方法是什么?

选项 2

一种方法可能是使用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)排序(似乎通过数组进行两个循环而不是一个)。但我担心机器会在这个过程中创建一些临时数组,这似乎是不必要的。

选项 3

使用wherewith怎么样UNPACK

where (M)
    A =A + UNPACK(B, M, 0.0d0)
end where

这似乎与选项 2 大致相同(两个循环,可能会创建临时数组)。它还必须M=.false.用 's 填充 UNPACK 数组的元素,0这似乎完全是浪费。

选项 4

在我的情况下.true.,掩码的元素通常是连续的块(即连续几个真,然后是一堆假,然后是另一个真块,等等)。也许这可能会导致类似于选项 1 的结果。假设有K这些.true.块。我需要一个数组indstart(大小K)给出A每个真实块开始的索引,以及一个具有真实块长度的索引blocksize(大小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例如可能有负值)。所以这个选项可能不会产生矢量化的结果。

--

关于这样做的好方法有什么想法吗?在我的情况下,数组indarrayMindstartblocksize将被创建一次,但添加操作必须针对不同的数组AB(尽管这些数组将具有恒定大小)进行多次。该where声明似乎可能是相关的。

标签: arraysalgorithmindexingfortranfortran90

解决方案


推荐阅读