OpenCL 的 enqueueWriteBuffer 导致 __memcpy_sse2_unaligned 分段错误

OpenCL's enqueueWriteBuffer causes __memcpy_sse2_unaligned segmentation fault

我有以下 OpenCL 代码,使用 C++ Wrapper 和英特尔的 OpenCL 工具包:

#include <Eigen/StdVector>

...

typedef Sample_t float
typedef std::vector<Sample_t, Eigen::aligned_allocator<Sample_t> > SampleArray;

...

SampleArray data(ns * nt);

...

mdata = cl::Buffer(context, CL_MEM_READ_ONLY, sizeof(Sample_t) * data.size());
queue.enqueueWriteBuffer(mdata, CL_FALSE, 0, sizeof(Sample_t) * data.size(), &data[0]);

当使用 -O3、march=native 和 mtune=native 标志编译时,它会导致来自 TBB 代码的以下分段错误:

__memcpy_sse2_unaligned() at memcpy-sse2-unaligned.S:116 0x7ffff6e64ba4

没有任何优化,程序运行良好。

我将问题追溯到 queue.enqueueWriteBuffer 调用,没有它我没有任何问题。

我试图注释掉修改变量 "data" 的部分代码,以防我访问无效的内存位置,但问题仍然存在。

如果我从 std::vector 中删除 aligned_allocator,没有优化的构建也会开始崩溃。

我总共有 70MB 试图存储在此缓冲区中,远低于 CL_DEVICE_MAX_MEM_ALLOC_SIZE 报告的 3.8GB。但是如果我减小数组的大小,问题就会停止。我在后面的案例中尝试的尺寸是 5。

我还决定打印 vector 分配的地址,它是 0x7f21b797f010,所以它至少对齐到 16 个字节。

编辑:关于多线程,数组的创建以及 OpenCL 操作发生在同一个方法和主线程中。命令队列不是用异步标志创建的,缓冲区写入后有一个flush()操作。

可能是什么问题?

谢谢

正如在评论中的对话中所确认的,这里的问题是 enqueueWriteBuffer() 操作是非阻塞的(CL_FALSE 作为 阻塞 参数传递) 并且源缓冲区(SampleArray 向量)在底层复制操作保证完成之前超出范围。

至少有 4 种可能的解决方案:

  1. 使用enqueueWriteBuffer()的阻塞形式。如文档所示,在这种情况下,一旦函数 returns 将不会访问源缓冲区。
  2. 捕获返回的事件并在 SampleArray() 超出范围之前调用 clWaitForEvents() 或调用 clFinish()。如果您的程序在此期间做任何实质性的事情,这仅比阻塞变体更可取。
  3. 将源数据保留足够长的时间。
  4. 不要使用 enqueueWriteBuffer() 的复制形式:创建一个带有 NULL 源的缓冲区,将其映射到应用程序的内存中 space,将数据写入其中,然后取消映射。这可能避免完全复制,至少在集成 GPUs/APUs)

这些大致按 parallelism/efficiency 的升序排列。