2

假设我有stl::array<float, 24> foo一个列主格式 arrayfire 数组的线性化 STL 挂件,例如af::array bar = af::array(4,3,2, 1, f32);. 所以我有一个尺寸为的af::dim4对象,最多有 4 个对象,并且我有线性化数组。dimsbaraf::seqfoo

如何明确地获得表示例如第 2.nd 和 3.rd 行的索引foo(即 的线性化版本bar),即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 不允许在与 GPU 后端链接时仅将数据存储在主内存(CPU 上下文)中。由于我有大量数据需要逐个处理并且 VRAM 非常有限,因此我想af::array从始终驻留在主内存中的 stl 容器临时实例化 -objects。

当然,我知道我可以编写一些索引魔法来解决我的问题,但我想使用相当复杂af::seq的对象,这可能会使索引逻辑的有效实现变得复杂。

4

1 回答 1

1

在与 Gitter 上的 Pavan Yalamanchili 讨论之后,我设法获得了一段我想分享的工作代码,以防其他人只需要将他的变量保存在 RAM 中并将使用时复制的部分复制到 VRAM,即 Arrayfire 宇宙(如果链接到 GPU 或 Nvidia 上的 OpenCL)。

这个解决方案也将帮助任何在他的项目中的其他地方使用 AF 并且希望有一种方便的方式访问具有 (N<=4) 的大型线性化 N-dim 数组的人。

//  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 上运行 for 循环,据说速度很慢,但到目前为止,如果 on 想要将数据专门保存在 CPU 上下文中并仅与 GPU 上下文共享部分,那么它确实是最好的方法AF 进行处理。

如果有人知道加快此代码速度的方法,我仍然愿意提供建议。

于 2017-01-20T18:05:08.327 回答