环境:Ubuntu18.04,OneAPI beta 6
完整的代码在下面,但是下面是一个令人反感的错误:
#dpcpp -O2 -g -o so2 so2.cpp -lOpenCL -lsycl
so2.cpp:64:38: error: cannot cast from type 'global_ptr<int>' (aka 'multi_ptr<int, access::address_space::global_space>') to pointer type 'int (*)[nelem]'
int (*xptr)[nelem] = (int (*)[nelem])xaccessor.get_pointer();
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 error generated.如果你想知道为什么..。
在开发数据并行代码时,我经常开发Intel以前称为“元素函数”的程序。编写这些代码是为了对应用程序的单个元素( SYCL称为工作项)进行操作。我一直认为这是一种简单易用的基本SW开发环境,易于测试,而且更易于重用(标量、SIMD、CUDA等)。
在对单个元素进行测试之后,通过扩展调用代码而不必重写/重新测试函数,迁移到数据并行非常容易:
int x[NELEM]
fn1(x, NELEM)变成了
int x[NPROC][NELEM]
for (int p=0; p<NPROC; p++) fn1(x[p], NELEM);在SYCL内核中,fn1(xitem.get_linear_id(),NELEM)将是我所需要的全部,而无需重新调用函数来理解I和/或访问器。
上述代码的SYCL问题是,在内核C++中,我似乎无法重新构造指向2D指针的访问器指针。这在应用程序C++中是允许的(参见上面的代码)。
也许这是正确代码的坏方法,但它使开发/测试代码变得容易,这些代码可以用于标量和数据并行编码,并使库具有一定的可移植性。它还为缓冲区/访问器提供了绕过SYCL 3维限制的方法。
总之,我很好奇一个真正的SYCL程序员会怎么想。
玩具示例的完整代码:
#include <CL/sycl.hpp>
#include <cstdio>
namespace sycl = cl::sycl;
const int Nproc=3;
const int Nelem=4;
/** elemental function **/
void
fn1(int *h, int n)
{
for (int i=0; i<n; i++) h[i] = 10*h[i]+2*i;
}
int
main(int argc, char *argv[])
{
/** Make some memory **/
int x1d[Nproc * Nelem];
for (int j=0; j<Nproc; j++) {
for (int i=0; i<Nelem; i++) x1d[j*Nelem+i] = 10*j+i;
}
printf("1D\n");
for (int i=0; i<Nelem; i++) {
printf("%d : ", i);
for (int j=0; j<Nproc; j++) printf("%d ", x1d[j*Nelem+i]);
printf("\n");
}
/** Reshape it into 2D **/
int (*x2d)[Nelem] = (int (*)[Nelem])x1d;
for (int j=0; j<Nproc; j++) fn1(x2d[j], Nelem);
printf("2D\n");
for (int i=0; i<Nelem; i++) {
printf("%d : ", i);
for (int j=0; j<Nproc; j++) printf("%d ", x2d[j][i]);
printf("\n");
}
/** SYCL setup **/
sycl::device dev = sycl::default_selector().select_device();
std::cout << "Device: "
<< "name: " << dev.get_info<sycl::info::device::name>() << std::endl
<< "vendor: " << dev.get_info<sycl::info::device::vendor>() << std::endl;
sycl::queue q(dev);
{
sycl::buffer<int, 1> xbuffer(x1d, sycl::range<1> {Nproc*Nelem});
q.submit([&](sycl::handler& cgh) {
int nelem = Nelem;
auto xaccessor = xbuffer.get_access<sycl::access::mode::read_write, sycl::access::target::global_buffer>(cgh);
cgh.parallel_for<class k0>(
sycl::range<1> {Nproc},
[=] (sycl::item<1> item) {
int idx = item.get_linear_id();
#if 0
int *xptr = (int *)xaccessor.get_pointer(); // doing this does work so we _can_ get a real pointer
fn1(xptr + nelem*idx, nelem);
#else
int (*xptr)[nelem] = (int (*)[nelem])xaccessor.get_pointer();
//int *ptr = (int *)xaccessor.get_pointer(); // splitting it into two doesn't work either
//int (*xptr)[nelem] = (int (*)[nelem])ptr;
fn1(xptr[idx], nelem);
#endif
}
);
}
);
}
printf("2D SYCL\n");
for (int i=0; i<Nelem; i++) {
printf("%d : ", i);
for (int j=0; j<Nproc; j++) printf("%d ", x1d[j*Nelem+i]);
printf("\n");
}
}编辑1:
帕卢哈德的评论,我试图充实一些备选方案。
首先,这两句评论似乎应该按照他的建议去做:
int *ptr = (int *)xaccessor.get_pointer();
int (*xptr)[nelem] = (int (*)[nelem])ptr;但实际上,它会产生这样的错误:
error: cannot initialize a variable of type 'int (*)[nelem]' with an rvalue of type 'int (*)[nelem]'
int (*xptr)[nelem] = (int (*)[nelem])ptr;
^ ~~~~~~~~~~~~~~~~~~~将"get()“添加到get_pointer的末尾会产生相同的结果。
奇怪的是,解决了错误的“初始化”部分:
int *ptr = (int *)xaccessor.get_pointer().get();
int (*xptr)[nelem];
xptr = (int (*)[nelem])ptr;产生有趣的错误:
error: incompatible pointer types assigning to 'int (*)[nelem]' from 'int (*)[nelem]'
xptr = (int (*)[nelem])ptr;
^~~~~~~~~~~~~~~~~~~所以如果某人有时间,我还是很好奇.
发布于 2020-05-19 15:12:04
简短回答:不是SYCL问题;)
根据编辑1,很明显,如果行
int *ptr = (int *)xaccessor.get_pointer();
int (*xptr)[nelem] = (int (*)[nelem])ptr;在第二行中引起转换错误,这不可能是DPC++/SYCL问题,因为只涉及int指针的变化,而且这里没有任何与SYCL相关的内容。
事实上,问题是nelem不是编译时常量.因此,下面的非SYCL测试程序
int main(){
int nelem = 10;
int* ptr = nullptr;
int (*xptr)[nelem] = (int (*)[nelem])ptr;
}在用常规clang编译或用-pedantic编译gcc时复制您的问题。但是,默认情况下,gcc支持可变长度数组作为C++中的扩展,因此即使代码不是有效的C++,代码也会进行编译。
按照nelem的要求,通过将C++转换为编译时常量可以解决问题。可变长度数组是较新版本的C的一部分,但不是C++的一部分。
https://stackoverflow.com/questions/61855921
复制相似问题