2016-12-05 3 views
2

Предположим, у меня есть stl::array<float, 24> foo, который является линеаризованным подвесом STL в массив массивных массивов формата Column-Major. af::array bar = af::array(4,3,2, 1, f32);. Поэтому у меня есть объект af::dim4dims с размерами bar, у меня есть до 4 af::seq -объектов, и у меня есть линеаризованный массив foo.Как явно получить линейные индексы от arrayfire?

Как можно явно указать индексы foo (то есть линеаризованная версия bar), представляющая, например, 2.nd и 3.rd row, то есть 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 есть простой способ.

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

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

+0

Зачем нужны линейные индексы в этом случае? Если вы можете показать какой-то код о том, что вы планируете делать после того, как получите линейные индексы, которые будут великолепны. –

ответ

1

После обсуждения с Паваном Яламанчили на 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*O 


int 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 -> VRAM 


    af_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 и якобы медленно из-за цикл работы над global_idx, но до сих пор его действительно лучшее, что можно сделать, если на хочет хранить данные в контексте CPU исключительно и доли только части с графическим интерфейсом AF для обработки.

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

Смежные вопросы