如何在arrayfire中使用flip和transpose来避免memcpy?

时间:2016-12-01 15:14:36

标签: c++ memory-management arrayfire

我使用arrayfire在GPU(OpenCL)的帮助下加速了一些C ++代码。我有600MB以上的af :: array,我需要沿着列维度翻转然后转置它。

到目前为止,我几乎使用C ++例程完成了这些操作。我现在喜欢用AF做这件事,但注意到AF库的内存使用过多。我有两个问题:

1)我完全不明白为什么300MB阵列上的任何操作(例如翻转或T)应该使用超过900MB的内存。   2)我想知道如何避免创建数组foo的副本。我认为通过将操作封装在一个单独的函数中,我会删除任何副本。

我有这样的代码:

void prepare_array(af::array &a) {
  af::array b = af::flip(a, 1);              // ~1400MB
  a = b.T();                                 // ~3000MB
}

af::array foo = af::randn(768,16384,3,1,c64); // ~300MB
prepare_array(foo);
af::deviceGC();                              // ~600MB

我只需要执行一次此操作,因此速度不如内存使用重要,但我宁愿在AF框架内执行此操作。

(所有内存使用情况统计信息都是使用debian上的NVIDIA内核驱动程序包中的gpustat读取的。)

CPU后端的内存使用量也过多。

感谢回复umar-arshad:当我在上次运行代码时,我在内存中运行代码 - 假设它的行为相同。我仔细检查了GPU上的测量结果并同时使用了gpustat和nvidia-smi。实际上,测量的代码与您解释的不同。现在它完全有意义 - 至少是GPU部分。

也许在CPU foo上起初只有f64,因为只使用了真实部分,并且通过使用翻转或转置它变为c64。

"分配触发隐式设备在某些平台上的所有队列上同步的事实"连同本网站:http://forums.accelereyes.com/forums/viewtopic.php?f=17&t=43097&p=61730&hilit=copy+host+memory+into+an+array#p61727 和af :: printMemInfo(); 帮助我最终弄清楚AF的大部分内存处理。加快我的计划。

但是现在仍然是使用这两个操作的唯一替代方法(或者尽可能少的开销):

// Generate/store data in std::array<af::cdouble> foo_unwrap = new af::cdouble[768*16384*3*1];

// Flip/Transpose foo_unwrap in plain C/C++, like in:
// for(column = 0; column < max_num_column/2; column++)
//   swap column with max_num_column-1-column
//
// http://www.geeksforgeeks.org/inplace-m-x-n-size-matrix-transpose/
//   but for Column-Major Order matrices
//
// and afterwards whenever needed do ad-hoc:
af::cdouble* first_elem = (af::cdouble*) &(foo_unwrap[0]); // to ensure correct type detection via AF
af::array foo = af::array(768,16384,3,1, first_elem, afDevice);

然而,这非常麻烦,因为我不想打扰Row- / Column-Major格式和索引魔法。所以我现在仍在寻找建议。

1 个答案:

答案 0 :(得分:2)

ArrayFire使用内存管理器来避免不必要的分配和解除分配。这是必需的,因为所有分配都会在某些平台上的所有队列上触发隐式设备同步。这可能会非常昂贵,因此ArrayFire将跟踪超出范围的af::array并在必要时重用它们。 ArrayFire还在启动时分配内存。

在你的情况下,你在randu调用中分配~600MB(c64是一个复数的双精度,因此每个元素是16个字节)。翻转操作还有另外600MB的分配,存储在b中。 Transpose将分配600MB,但会保留较旧的值以供重用。此时,由于这些操作,您分配了大约1800 MB的内存。

当您从prepared_array函数调用返回时,b将超出范围并将被标记为删除。此时,您有3个缓冲区,每个600MB。其中两个缓冲区未使用,但ArrayFire可以在将来的操作中使用这些缓冲区。一旦调用deviceGC函数,两个未使用的数组都将被释放,尽管你可能会分配相似大小的数组,这样可以保留它们。

您可以使用af::printMemInfo()功能跟踪ArrayFire所做的分配。

Disclamer:我是ArrayFire的开发人员之一。