标签: sycl

理解 AMD GPU 中的 oneAPI 和 SYCL

我是一名 GPGPU 开发人员,我使用 CUDA 完成了所有工作。最近,我为我的家庭设置购买了 AMD GPU,并且我一直在阅读有关 SYCL 和 oneAPI 的文章,但我不确定我是否理解它们是什么,它们是如何互补的。如果我理解正确的话,SYCL 是 Khronos 开发的一个标准,它允许在 C++ 中创建并行软件。它需要一个由 SYCL 实现提供的自定义工具链,并且存在多个实现。

另一方面,oneAPI 是 SYCL 的一个实现,带有一些额外的扩展(将来可能会添加到 SYCL 标准中)和一些带有典型并行库的库,对吗?

因为我有一个AMD GPU,我想用它来做一些GPGPU,但生态系统有点吓人。我们有台面驱动程序、AMD 驱动程序、不同的 openCL 实现、HIP ……这一切是如何组合在一起的?

amd-gpu hip sycl intel-oneapi

10
推荐指数
1
解决办法
1781
查看次数

使用统一共享内存而不是设备内存时,SYCL 性能下降 4000%

在SYCL中,内存分为三种类型:主机内存、设备内存和统一共享内存(USM)。对于主机和设备内存,数据交换需要显式复制。同时,进出 USM 的数据移动由 SYCL 运行时自动隐式管理。

sycl::malloc_device()不幸的是,在使用 SYCL 为数值内核实现 GPU 加速的过程中,我发现仅通过切换到到,性能就会下降高达 4000% sycl::malloc_shared()- 即使我所做的只是重复重新提交相同的 SYCL 内核,而不做任何尝试从主机访问数据。

当使用针对 AMD HIP GFX906 (Radeon VII / Instinct MI50) 的 OpenSYCL 构建代码时sycl::malloc_device(),程序在 0.27 秒内完成:

$ time ./fdtd.elf 
simulate 16974593 cells for 10 timesteps.

real    0m0.271s
user    0m0.253s
sys     0m0.020s
Run Code Online (Sandbox Code Playgroud)

当使用 构建相同的代码时sycl::malloc_shared(),程序需要 10.6 秒才能完成:

simulate 16974593 cells for 10 timesteps.

real    0m10.649s
user    0m15.172s
sys     0m0.196s
Run Code Online (Sandbox Code Playgroud)

这是 3925% 的减速。

在BIOS中启用“4G以上解码”和“可调整BAR大小”支持后,现在需要3.8秒而不是10.6秒。但这并不能解决不必要的内存传输的实际问题 - 1300% 的性能损失仍然相当显着。

我之前也使用英特尔 DPC++ 编译器测试了类似的内核,并在相同的硬件上看到了类似的结果。

我怀疑速度减慢是由不必要的主机和设备复制引起的,但我不确定。SYCL 运行时使用什么启发法来确定是否需要复制?

下面附有示例代码。

ArrayNXYZ.hpp:4维数组(n,x,y,z)包装类。 …

c++ hpc gpgpu sycl dpc++

10
推荐指数
1
解决办法
663
查看次数

对浮点数求和的最佳 OpenCL 2 内核是什么?

C++ 17 引入了许多新算法来支持并行执行,特别是std::reduce是std::accumulate的并行版本,它允许浮点加法等操作non-deterministic的行为。non-commutative我想使用 OpenCL 2 实现一个归约算法。

Intel这里有一个示例,它使用 OpenCL 2work group内核函数来实现std::exclusive_scan OpenCL 2 内核。以下是基于英特尔exclusive_scan示例的内核求和浮点数:

kernel void sum_float (global float* sum, global float* values)
{
  float sum_val = 0.0f;

  for (size_t i = 0u; i < get_num_groups(0); ++i)
  {
    size_t index = get_local_id(0) + i * get_enqueued_local_size(0);
    float value = work_group_reduce_add(values[index]);
    sum_val += work_group_broadcast(value, 0u);
  }

  sum[0] = sum_val;
}
Run Code Online (Sandbox Code Playgroud)

上面的内核可以工作(或者看起来可以!)。然而,exclusive_scan要求work_group_broadcast函数将最后一个值 1 传递work …

c++ gpgpu opencl c++17 sycl

5
推荐指数
1
解决办法
1076
查看次数

在 MacOS 中使用 SYCL 1.2

我想开始使用SYCL,但目前发现需要安装ComputeCpp,而且只支持Ubuntu、CentOS和Windows。在 MacOS Catalina 上使用 SYCL 的替代方法有哪些?

macos sycl

4
推荐指数
1
解决办法
882
查看次数

如何在 SYCL parallel_for(内核)内部打印?

我正在开发与 oneAPI 基础套件一起提供的 vectorAdd 示例应用程序。当我尝试在内核中打印总和时,出现以下错误。

请查看附件中的源代码以及编译时的错误。

//Source code 

    cgh.parallel_for<class VectorAdd>(num_items, [=](id<1> wiID) {
        sum_accessor[wiID] = addend_1_accessor[wiID] + addend_2_accessor[wiID];

        std::cout<<"Sum : "<<sum_accessor[wiID]<<std::endl;  // I want to print this sum

        });
Run Code Online (Sandbox Code Playgroud)

在编译期间我收到以下错误。

 //Error generated while compiling

usr/lib/gcc/x86_64-linux-gnu/7.4.0/../../../../include/c++/7.4.0/bits/ostream.tcc:359:25: error: SYCL kernel cannot have a class with a virtual function table
  extern template class basic_ostream<char>;
                        ^
vector-add.cpp:159:6: note: used here
            std::cout<<"Sum : "<<sum_accessor[wiID]<<std::endl;
            ^
vector-add.cpp:159:11: error: SYCL kernel cannot use a global variable
            std::cout<<"Sum : "<<sum_accessor[wiID]<<std::endl;
Run Code Online (Sandbox Code Playgroud)

sycl intel-oneapi dpc++

3
推荐指数
1
解决办法
2419
查看次数

在SYCL中使用一个缓冲区还是多个缓冲区更有效?

假设我有一组数据,例如大小为 N 的 3D 向量数组。假设我的 SYCL 内核的每次迭代专门或主要只涉及一个向量。一般来说,以下哪种将其分解为连续缓冲区的方法更有效——或者重要吗?

我意识到目标设备对此影响很大,所以我们假设它是一个独立的 GPU(即数据确实必须复制到不同的内存芯片,并且该设备没有像 FPGA 这样的疯狂架构 - 我是主要针对通过 CUDA 的 GTX 1080,但我预计当代码编译为 OpenCL 或我们使用另一个现代 GPU 时答案可能类似。

  1. 为每个坐标创建一个单独的缓冲区,例如sycl::buffer<float> x, y, z;,每个大小为 N。这样,在访问它们时,我可以使用sycl::id<1>传递给内核的 lambda 作为索引,无需进行数学运算。(我怀疑编译器可能能够对此进行优化。)
  2. 为所有这些创建一个打包缓冲区,例如sycl::buffer<float> coords;大小为 3N。sycl::id<1>当使用调用访问它们时i,我会获取 x 坐标buffer_accessor[3*i]、y 坐标buffer_accessor[3*i+1]和 z 坐标buffer_accessor[3*i+2]。(我不知道编译器是否可以对此进行优化,并且我不确定对齐问题是否会发挥作用。)
  3. 使用结构体创建一个解压缓冲区,例如struct Coord { float x,y,z; }; sycl::buffer<Coord> coords;. 由于对齐填充,这会增加内存使用量,在本例中增加 33%,这会带来相当惊人的成本,这也会增加将缓冲区复制到设备所需的时间。但代价是您可以在不操作 的情况下访问数据sycl::id<1>,运行时只需处理一个缓冲区,并且设备上不应该出现任何缓存行对齐效率低下的情况。
  4. 使用大小为 (N,3) 的二维缓冲区并仅在第一维的范围内进行迭代。这是一个不太灵活的解决方案,我不明白为什么当我不迭代所有维度时我想要使用多维缓冲区,除非针对此用例内置了大量优化。

我找不到任何关于数据架构的指南来获得对此类事情的直觉。现在(4)看起来很愚蠢,(3)涉及不可接受的内存浪费,我正在使用(2),但想知道我是否不应该使用(1)来避免 id 操作和 3*sizeof(float)对齐的访问块。

c++ memory-management sycl dpc++

3
推荐指数
1
解决办法
826
查看次数

SYCL设备选择器中的host_selector是什么?

我是SYCL,OpenCL和GPU编程的新手.我在SYCL中读到了设备选择器,发现了以下四个:

  1. default_selector:系统启发式选择的设备.如果未找到OpenCL设备,则默认为SYCL主机设备.
  2. gpu_selector:根据设备类型info :: device :: device_type :: gpu从所有可用的OpenCL设备中选择设备.如果未找到OpenCL GPU设备,则选择器将失败.
  3. cpu_selector:根据设备类型info :: device :: device_type :: cpu从所有可用设备和启发式中选择设备.如果未找到OpenCL CPU设备,则选择器将失败.
  4. host_selector:选择不需要OpenCL运行时的SYCL主机CPU设备.

我跑去computecpp_info找设备是:

$ /usr/local/computecpp/bin/computecpp_info
/usr/local/computecpp/bin/computecpp_info: /usr/local/cuda-8.0/lib64/libOpenCL.so.1: no version information available (required by /usr/local/computecpp/bin/computecpp_info)
/usr/local/computecpp/bin/computecpp_info: /usr/local/cuda-8.0/lib64/libOpenCL.so.1: no version information available (required by /usr/local/computecpp/bin/computecpp_info)
********************************************************************************

ComputeCpp Info (CE 0.7.0)

********************************************************************************

Toolchain information:

GLIBC version: 2.19
GLIBCXX: 20150426
This version of libstdc++ is supported.

********************************************************************************


Device Info:

Discovered 3 devices matching:
  platform    : <any>
  device type : <any>

--------------------------------------------------------------------------------
Device 0:

  Device is …
Run Code Online (Sandbox Code Playgroud)

gpu gpgpu opencl sycl

2
推荐指数
1
解决办法
293
查看次数

sycl/dpc++ 访问器与内核函数对象中的 global_ptr

使用英特尔 OneAPI beta6 的以下玩具代码。

#include <CL/sycl.hpp>
#include <iostream>

namespace sycl = cl::sycl;

const int SIZE=1;

class Increment_accessor {
  public:
    Increment_accessor(sycl::accessor<int, 1, sycl::access::mode::read_write, sycl::access::target::global_buffer> ptr_) : ptr {ptr_} {}
    void operator()(sycl::item<1> item) {
      ptr[item.get_linear_id()]++;
    }
  private:
    sycl::accessor<int, 1, sycl::access::mode::read_write, sycl::access::target::global_buffer> ptr;
};

class Increment_pointer {
  public:
    Increment_pointer(sycl::global_ptr<int> ptr_) : ptr {ptr_} {} 
    void operator()(sycl::item<1> item) {
      ptr[item.get_linear_id()]++;
    }
  private:
    sycl::global_ptr<int> ptr;
};

int 
main(int argc, char *argv[])
{
  sycl::device dev = sycl::default_selector().select_device();
  sycl::queue q(dev);
  int hbuffer[SIZE] = {};

  {
    sycl::buffer<int, 1> …
Run Code Online (Sandbox Code Playgroud)

sycl intel-oneapi dpc++

2
推荐指数
1
解决办法
336
查看次数

为什么 printf() 在内核中可以工作,但使用 std::cout 却不能?

我一直在探索并行编程领域,并用 Cuda 和 SYCL 编写了基本内核。我遇到过一种情况,我必须在内核内部打印,我注意到std::cout内核内部不起作用,而实际上却起作用printf。例如,考虑以下 SYCL 代码 - 这有效 -

void print(float*A, size_t N){
    buffer<float, 1> Buffer{A, {N}};
    queue Queue((intel_selector()));
    Queue.submit([&Buffer, N](handler& Handler){
       auto accessor = Buffer.get_access<access::mode::read>(Handler);
       Handler.parallel_for<dummyClass>(range<1>{N}, [accessor](id<1>idx){
           printf("%f", accessor[idx[0]]);
       });
    });
}
Run Code Online (Sandbox Code Playgroud)

printf而如果我用它替换std::cout<<accessor[idx[0]]它会引发编译时错误,并提示 - Accessing non-const global variable is not allowed within SYCL device code. CUDA 内核也会发生类似的情况。这让我思考,两者之间可能存在什么差异printf,以及std::coout是什么导致了这种行为。

另外假设如果我想实现一个从GPU调用的自定义打印函数,我应该怎么做?
TIA

printf cuda cout sycl

2
推荐指数
1
解决办法
1655
查看次数

DPC++ &amp; MPI、缓冲区、共享内存、变量声明

我是 DPC++ 的新手,我尝试开发一个基于 MPI 的 DPC++ 泊松解算器。我读了这本书,对缓冲区和指针与共享或主机内存感到非常困惑。这两件事有什么区别,我开发代码时应该使用什么。

现在,我使用由具有 const 大小的 std::array 初始化的缓冲区作为串行代码,并且效果良好。然而,当我将 DPC++ 代码与 MPI 耦合时,我必须为每个设备声明本地长度,但我没有这样做。这里我附上我的代码

    define nx 359
    define ny 359
    constexpr int local_len[2];
    global_len[0] = nx + 1;
    global_len[1] = ny + 1;

    for (int i = 1; i < process; i++)
    {
        if (process % i == 0)
        {
            px = i;
            py = process / i;
            config_e = 1. / (2. * (global_len[1] * (px - 1) / py + global_len[0] * (py - 1) / …
Run Code Online (Sandbox Code Playgroud)

c++ mpi sycl intel-oneapi dpc++

0
推荐指数
1
解决办法
187
查看次数

标签 统计

sycl ×10

dpc++ ×5

c++ ×4

intel-oneapi ×4

gpgpu ×3

opencl ×2

amd-gpu ×1

c++17 ×1

cout ×1

cuda ×1

gpu ×1

hip ×1

hpc ×1

macos ×1

memory-management ×1

mpi ×1

printf ×1