У меня есть два (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
Тем не менее, существует организация, занимающаяся этой проблемой, которая, по моему мнению, должна обеспечить лучшую производительность:
Не должно быть никаких преград для векторизации. То есть Обновления для каждого i
являются независимыми и могут быть выполнены в любом порядке.
Нет необходимости переходить назад и вперед в массиве A
. Машина должна знать, что нужно выполнить однократный цикл по массивам, обновляя только A
там, где это необходимо.
Временные массивы не нужны.
Какой лучший способ сделать это в Фортране?
Вариант 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
Как насчет использования where
с UNPACK
?
where (M)
A =A + UNPACK(B, M, 0.0d0)
end where
Это похоже на вариант 2 (два цикла и, возможно, создает временные массивы). Также необходимо заполнить элементы M=.false.
массива 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
может иметь отрицательные значения). Так что эта опция, вероятно, не приведет к векторизованному результату.
-
Есть какие-нибудь мысли о хорошем способе сделать это? В моей ситуации массивы indarray
, M
, indstart
и blocksize
будут созданы один раз, но операция добавления должна выполняться много раз для разных массивов A
и B
(хотя эти массивы будут иметь постоянные размеры). Кажется, что заявление where
может быть уместным.