SYCL/DPC++ cpu 版本给出了正确的结果,但是 gpu 给出了错误的数据
SYCL/DPC++ cpu version gives correct result, but gpu gives incorrect data
我使用 intel dpc++ 编译器编译并 运行 下面的代码。使用 cpu 选择器时我得到了正确的结果,但 gpu 选择器给出了垃圾值。
我的代码所做的只是将一个名为 data 的数组初始化为全 1。在 sycl 内核中,此数组的访问器乘以 3 并保存到结果数组。我尝试在结果数组中打印值,该数组应该全是 3,但我得到的是垃圾值。
因为我在 gpu 上执行代码时得到了垃圾值。我在 cpu 选择器上尝试了 运行,这里的代码没有问题。
我在 linux 和 windows 上试过这个。编译器版本 dpcpp 2021.3
#include "iostream"
#include<CL/sycl.hpp>
#include <array>
using namespace std;
using namespace sycl;
int main() {
sycl::gpu_selector selector;
//using cpu selector as in the line below works
//sycl::cpu_selector selector;
sycl::queue q = sycl::queue(selector);
std::cout << q.get_device().get_info<sycl::info::device::name>();
constexpr int size = 3;
std::array<int, size> data{1,1,1};
std::array<int, size> resultarray;
range<1> num_items{ size };
buffer<int, 1> data_buff(data.data(), num_items);
buffer<int, 1> result(resultarray.data(), num_items);
q.submit([&](sycl::handler& cgh)
{
auto dataAccess = data_buff.get_access<access::mode::read_write>(cgh);
auto resultAccess = result.get_access<access::mode::write>(cgh);
cgh.parallel_for(num_items, [=](id<1> i)
{
resultAccess[i] = dataAccess[i] * 3;
});
}).wait();
std::cout <<"||"<< resultarray[0]<<"||"; //expected result ||3||
}
有人可以帮助解释为什么代码会在 GPU 上给出错误的结果吗?
您没有触发复制回主机。大概在 CPU 上,您的 SYCL 实现只是决定直接对输入指针进行操作,所以您看不到问题。
想一想:SYCL 实现如何知道 resultarray
正在您的 cout
中使用并且必须将数据复制回来?它不能,因为此内存访问不通过任何 SYCL 构造。因此它不知道它必须将数据复制回来。 wait()
只会导致主机等待内核完成,不会触发副本。
触发必要副本的最重要方法是:
- 使用缓冲区写回:默认情况下,从主机指针构造的缓冲区会将其在缓冲区析构函数中的内容写回到它们构造时使用的数据指针(缓冲区中也有成员函数可手动 enable/disable 此功能)。在你的情况下,将缓冲区声明和内核包装在额外的
{ }
中就足够了,因为这样缓冲区就会在你的 cout
之前超出范围,并且回写被触发。
- 使用
host_accessor
而不是直接访问 resultarray
- 使用显式
handler::copy()
我使用 intel dpc++ 编译器编译并 运行 下面的代码。使用 cpu 选择器时我得到了正确的结果,但 gpu 选择器给出了垃圾值。
我的代码所做的只是将一个名为 data 的数组初始化为全 1。在 sycl 内核中,此数组的访问器乘以 3 并保存到结果数组。我尝试在结果数组中打印值,该数组应该全是 3,但我得到的是垃圾值。
因为我在 gpu 上执行代码时得到了垃圾值。我在 cpu 选择器上尝试了 运行,这里的代码没有问题。
我在 linux 和 windows 上试过这个。编译器版本 dpcpp 2021.3
#include "iostream"
#include<CL/sycl.hpp>
#include <array>
using namespace std;
using namespace sycl;
int main() {
sycl::gpu_selector selector;
//using cpu selector as in the line below works
//sycl::cpu_selector selector;
sycl::queue q = sycl::queue(selector);
std::cout << q.get_device().get_info<sycl::info::device::name>();
constexpr int size = 3;
std::array<int, size> data{1,1,1};
std::array<int, size> resultarray;
range<1> num_items{ size };
buffer<int, 1> data_buff(data.data(), num_items);
buffer<int, 1> result(resultarray.data(), num_items);
q.submit([&](sycl::handler& cgh)
{
auto dataAccess = data_buff.get_access<access::mode::read_write>(cgh);
auto resultAccess = result.get_access<access::mode::write>(cgh);
cgh.parallel_for(num_items, [=](id<1> i)
{
resultAccess[i] = dataAccess[i] * 3;
});
}).wait();
std::cout <<"||"<< resultarray[0]<<"||"; //expected result ||3||
}
有人可以帮助解释为什么代码会在 GPU 上给出错误的结果吗?
您没有触发复制回主机。大概在 CPU 上,您的 SYCL 实现只是决定直接对输入指针进行操作,所以您看不到问题。
想一想:SYCL 实现如何知道 resultarray
正在您的 cout
中使用并且必须将数据复制回来?它不能,因为此内存访问不通过任何 SYCL 构造。因此它不知道它必须将数据复制回来。 wait()
只会导致主机等待内核完成,不会触发副本。
触发必要副本的最重要方法是:
- 使用缓冲区写回:默认情况下,从主机指针构造的缓冲区会将其在缓冲区析构函数中的内容写回到它们构造时使用的数据指针(缓冲区中也有成员函数可手动 enable/disable 此功能)。在你的情况下,将缓冲区声明和内核包装在额外的
{ }
中就足够了,因为这样缓冲区就会在你的cout
之前超出范围,并且回写被触发。 - 使用
host_accessor
而不是直接访问resultarray
- 使用显式
handler::copy()