3

我在 GPU (OpenCL) 的帮助下使用 arrayfire 来加速一些 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 框架内执行此操作。

(所有内存使用统计数据都是使用 gpustat 从 debian 上的 NVIDIA 内核驱动程序包中读取的。)

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


感谢 umar-arshad 的回复:当我上次在 CPU 上运行代码时,我分析了内存使用情况——假设它的行为相同。我在 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 格式和索引魔法而烦恼。所以我仍然在这里寻找建议。

4

1 回答 1

3

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

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

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

af::printMemInfo()您可以使用该函数跟踪 ArrayFire 所做的分配。

Disclamer:我是 ArrayFire 的开发者之一。

于 2016-12-04T05:07:43.633 回答