Умный способ добавить массив в более длинный массив по определенным индексам в Фортране? - PullRequest
0 голосов
/ 05 сентября 2018

У меня есть два (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. Временные массивы не нужны.

Какой лучший способ сделать это в Фортране?

Вариант 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 может быть уместным.

...