在 OpenCL 结构中使用矢量数据类型会导致 Visual Studio 中的 Watch 问题

Using vector data types in OpenCL structs causes Watch problems in Visual Studio

考虑以下 OpenCL 示例

typedef struct MyStruct
{
  float value;

  // Causes wrong memory alignment
  int2 position;
} MyStruct;


__kernel void foo()
{
   MyStruct structs[10];

   for (int i = 0; i < 10; i++)
   {
      int2 pos = { i, i };
      MyStruct newStruct = { i * i, pos };
      structs[i] = newStruct;
   }
}    

当我使用矢量数据类型时,例如 OpenCL 结构中的 int2,我在将项目(结构)添加到数组后发生内存损坏。下一项的成员被奇怪的值覆盖(我假设内存填充)。

在下面的屏幕截图中,您可以看到循环第一次迭代后的调试值。红色是所有更改的值。将第一个结构项添加到数组中,导致更改第二个项的值成员。

在第二次迭代中,第三项的值设置为 'unknown' 值。但是第二项的position值也是错误的

谁能解释一下这种奇怪的行为?但是,当我用两个 int 成员替换 int2 成员时,一切都按预期进行。对我来说,这似乎是向量数据类型的内存对齐错误。

编辑

Intel CPU 上的代码是 运行,安装了最新的 Intel SDK。 运行 CPU 设备上的 OpenCL 代码是通过设置断点调试内核的唯一可能。 IDE 是 Visual Studio 2015 Update 3,带有适用于 OpenCL 的英特尔插件。

已解决

这似乎是 Visual Studio 的 Watch window 中的一个 bug。在 OpenCL 内核中调试具有矢量数据类型成员的结构时显示错误值。

我已经在我的 GPU 上验证了你的代码,它没有问题。

你要的东西do/check:

1.Make确保您安装了最新的 OpenCL 驱动程序和图形驱动程序。

2.Make 确定这不是 "Watch" 问题。将数据复制到 CPU 并检查。例如:

#!/usr/bin/env python

import numpy as np
import pyopencl as cl

a_np = np.zeros(10).astype(np.float32)
b_np = np.zeros(20).astype(np.int32)

ctx = cl.create_some_context()
queue = cl.CommandQueue(ctx)

mf = cl.mem_flags
a_g = cl.Buffer(ctx, mf.READ_WRITE | mf.USE_HOST_PTR, hostbuf=a_np)
b_g = cl.Buffer(ctx, mf.READ_WRITE | mf.USE_HOST_PTR, hostbuf=b_np)

prg = cl.Program(ctx, """
typedef struct MyStruct
{
  float value;

  // Causes wrong memory alignment
  int2 position;
} MyStruct;

__kernel void foo(
    __global float *a_g, __global int *b_g)
{
   MyStruct structs[10];

   for (int i = 0; i < 10; i++)
   {
      int pos = { i, i };
      MyStruct newStruct = { i * i, pos };
      structs[i] = newStruct;
   }


   for (int i = 0; i < 10; i++)
   {
      a_g[i] = structs[i].value;
      b_g[2*i] = structs[i].position.x;
      b_g[2*i+1] = structs[i].position.y;
   }
}
""").build()

prg.foo(queue, (1,1), (1,1), a_g, b_g)

cl.enqueue_copy(queue, a_np, a_g)
cl.enqueue_copy(queue, b_np, b_g)

print a_np
print b_np

输出:

[  0.   1.   4.   9.  16.  25.  36.  49.  64.  81.]
[0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9]

一定是对齐问题。根据设备和主机,它可能会或可能不会工作,因此您需要确保对齐在主机和设备端都明确兼容。

如果你能看到结果,它已经被复制到内存中。将结果复制到 RAM 时,应确保使用右对齐。 in2 有 8 字节对齐,而两个 int 各有 4 字节对齐。第一项(value)也是4个字节。

在 opencl 设备中,您可以自由使用结构中的任何字段顺序,但如果您要在 RAM 上使用它,则需要两边都具有兼容性。这种兼容性通常是通过在结构中进行一些简单的编辑来实现的。按对齐方式排序字段就是其中之一。示例:

  • 排列尺寸从最大到最小的顺序(最大的在前)。因为硬件使用特殊的内存操作优化来快速读取结构,这使得它在 RAM 上不为人所知。只有设备知道它,除非您明确订购字段。示例:

(结构)

int2 var0;  // biggest alignment (8)
float var1; // smallest alignment (4)

another example:  

int4 var0;
float4 var1;
int2 var2;
int2 var3;
int2 var4;
char var5;  // 57 bytes total
char dummy1; // to have struct size equal to power of 2.
char dummy2; // to have struct size equal to power of 2.
char dummy3; // to have struct size equal to power of 2.
char dummy4; // to have struct size equal to power of 2.
char dummy5; // to have struct size equal to power of 2.
char dummy6; // to have struct size equal to power of 2.
char dummy7; // to have struct size equal to power of 2.