Как явно получить линейные индексы из arrayfire?

Предположим, у меня есть stl::array<float, 24> foo который является линеаризованным STL-подвесом к массиву массива огня формата Column-Major, например, af::array bar = af::array(4,3,2, 1, f32);, Итак, у меня есть af::dim4 объект dims с размерами barУ меня до 4 af::seq-объекты и у меня есть линеаризованный массив foo,

Как можно явно получить показатели foo (то есть линеаризованная версия bar) представляющий, например, 2-й и 3-й ряд, т.е. bar(af::seq(1,2), af::span, af::span, af::span)? У меня есть небольшой пример кода, приведенный ниже, который показывает, что я хочу. В конце я также объясняю, почему я этого хочу.

af::dim4 bigDims = af::dim4(4,3,2);
stl::array<float, 24> foo;   // Resides in RAM and is big
float* selBuffer_ptr;        // Necessary for AF correct type autodetection
stl::vector<float> selBuffer;
// Load some data into foo
af::array selection;         // Resides in VRAM and is small

af::seq selRows = af::seq(1,2);
af::seq selCols = af::seq(bigDims[1]);   // Emulates af::span
af::seq selSlices = af::seq(bigDims[2]); // Emulates af::span
af::dim4 selDims = af::dim4(selRows.size, selCols.size, selSlices.size);

dim_t* linIndices;
// Magic functionality getting linear indices of the selection
//  selRows x selCols x selSlices

// Assign all indexed elements to a consecutive memory region in selBuffer
// I know their positions within the full dataset, b/c I know the selection ranges.

selBuffer_ptr = static_cast<float> &(selBuffer[0]);

selection = af::array(selDims, selBuffer_ptr);      // Copies just the selection to the device (e.g. GPU)

// Do sth. with selection and be happy
// I don't need to write back into the foo array.

В Arrayfire должна быть реализована такая логика для доступа к элементам, и я нашел несколько связанных классов / функций, таких как af::index, af::seqToDims, af::gen_indexing, af::array::operator() — однако я еще не мог найти легкий выход.

Я думал о том, чтобы в основном реализовать operator(), чтобы он работал аналогично, но не требовал ссылки на объект массива. Но это может быть потрачено впустую, если есть простой способ в arrayfire-framework.

Фон:
Причина, по которой я хочу это сделать, заключается в том, что arrayfire не позволяет хранить данные только в основной памяти (в контексте ЦП), будучи привязанным к бэкэнду графического процессора. Поскольку у меня есть большой кусок данных, которые нужно обрабатывать только по частям, а VRAM довольно ограничен, я хотел бы создать экземпляр af::array-объекты ad-hoc из stl-контейнера, который всегда находился в основной памяти.

Конечно, я знаю, что могу запрограммировать индексную магию, чтобы обойти мою проблему, но я бы хотел использовать довольно сложную af::seq объекты, которые могут усложнить эффективную реализацию индексной логики.

2

Решение

После беседы с Паваном Яламанчили о Gitter мне удалось получить рабочий фрагмент кода, которым я хочу поделиться, если кому-то еще понадобится хранить свои переменные только в оперативной памяти и копировать их при использовании в VRAM, то есть в юниверс Arrayfire. (если связано с OpenCL на GPU или Nvidia).

Это решение также поможет всем, кто использует AF где-то еще в своем проекте и кто хочет иметь удобный способ доступа к большому линеаризованному массиву N-dim с помощью (N<= 4).

//  Compile as: g++ -lafopencl malloc2.cpp && ./a.out
#include <stdio.h>
#include <arrayfire.h>
#include <af/util.h>

#include <cstdlib>
#include <iostream>

#define M 3
#define N 12
#define O 2
#define SIZE M*N*Oint main() {
int _foo;                      // Dummy variable for pausing program
double* a = new double[SIZE];  // Allocate double array on CPU (Big Dataset!)
for(long i = 0; i < SIZE; i++) // Fill with entry numbers for easy debugging
a[i] = 1. * i + 1;

std::cin >> _foo; // Pause

std::cout << "Full array: ";
// Display full array, out of convenience from GPU
// Don't use this if "a" is really big, otherwise you'll still copy all the data to the VRAM.
af::array ar = af::array(M, N, O, a);   // Copy a RAM -> VRAMaf_print(ar);

std::cin >> _foo; // Pause// Select a subset of the full array in terms of af::seq
af::seq seq0 = af::seq(1,2,1);     // Row 2-3
af::seq seq1 = af::seq(2,6,2);     // Col 3:5:7
af::seq seq2 = af::seq(1,1,1);     // Slice 2// BEGIN -- Getting linear indices
af::array aidx0 = af::array(seq0);
af::array aidx1 = af::array(seq1).T() * M;
af::array aidx2 = af::reorder(af::array(seq2), 1, 2, 0) * M * N;

af::gforSet(true);
af::array aglobal_idx = aidx0 + aidx1 + aidx2;
af::gforSet(false);

aglobal_idx = af::flat(aglobal_idx).as(u64);
// END -- Getting linear indices

// Copy index list VRAM -> RAM (for easier/faster access)
uintl* global_idx = new uintl[aglobal_idx.dims(0)];
aglobal_idx.host(global_idx);

// Copy all indices into a new RAM array
double* a_sub = new double[aglobal_idx.dims(0)];
for(long i = 0; i < aglobal_idx.dims(0); i++)
a_sub[i] = a[global_idx[i]];

// Generate the "subset" array on GPU & diplay nicely formatted
af::array ar_sub = af::array(seq0.size, seq1.size, seq2.size, a_sub);
std::cout << "Subset array: ";  // living on seq0 x seq1 x seq2
af_print(ar_sub);

return 0;
}

/*
g++ -lafopencl malloc2.cpp && ./a.out

Full array: ar
[3 12 2 1]
1.0000     4.0000     7.0000    10.0000    13.0000    16.0000    19.0000    22.0000    25.0000    28.0000    31.0000    34.0000
2.0000     5.0000     8.0000    11.0000    14.0000    17.0000    20.0000    23.0000    26.0000    29.0000    32.0000    35.0000
3.0000     6.0000     9.0000    12.0000    15.0000    18.0000    21.0000    24.0000    27.0000    30.0000    33.0000    36.0000

37.0000    40.0000    43.0000    46.0000    49.0000    52.0000    55.0000    58.0000    61.0000    64.0000    67.0000    70.0000
38.0000    41.0000    44.0000    47.0000    50.0000    53.0000    56.0000    59.0000    62.0000    65.0000    68.0000    71.0000
39.0000    42.0000    45.0000    48.0000    51.0000    54.0000    57.0000    60.0000    63.0000    66.0000    69.0000    72.0000

ar_sub
[2 3 1 1]
44.0000    50.0000    56.0000
45.0000    51.0000    57.0000
*/

Решение использует некоторые недокументированные функции AF и предположительно медленное из-за цикла for, выполняющегося над global_idx, но пока его действительно лучше всего сделать, если on хочет хранить данные исключительно в контексте CPU и совместно использовать только части с контекстом GPU AF для обработки.

Если кто-нибудь знает способ ускорить этот код, я все еще открыт для предложений.

1

Другие решения

Других решений пока нет …