CUDA 推力函子,在 CPU 或 GPU 中具有 运行 的灵​​活性

CUDA Thrust Functor with Flexibility to Run in CPU or GPU

这可能是一个愚蠢的问题,但我似乎无法找到任何与之相关的资源,因此非常感谢您的意见。

假设我有一些仿函数

struct AddOne {
    thrust::device_ptr<int> numbers;

    __device__
    void operator()(int i) {
        numbers[i] = numbers[i] + 1;
    }
}

我可以打电话给

AddOne addOneFunctor;
thrust::device_vector<int> idx(100), numbers(100);
addOneFunctor.numbers = numbers.data();
thrust::sequence(idx.begin(), idx.end(), 0);
thrust::for_each(thrust::device, idx.begin(), idx.end(), addOneFunctor);

是否可以编写上面的代码,以便可以在编译时或理想情况下 运行-time 更改执行策略?

例如将结构更改为

struct AddOne {
    thrust::pointer<int> numbers;

     __host__ __device__
     void operator()(int i) {
         numbers[i] = numbers[i] + 1;
     }
}

所以它可以是运行类似

的东西
AddOne addOneFunctor;
std::vector<int> idx(100), numbers(100);
addOneFunctor.numbers = numbers.data();
thrust::sequence(idx.begin(), idx.end(), 0);
thrust::for_each(thrust::cpp::par, idx.begin(), idx.end(), addOneFunctor);

底线是:我想要一个单一的代码库,我可以在其中决定使用 thrust::device_vectors 或某种宿主向量(例如 std::vectors)和运行 这些分别在 GPU 中(使用 thrust::device exec 策略)或 CPU(使用 thrust::cpp::par 或类似策略)。

PS:我想暂时避免使用 PGI。

这符合您的要求吗?

  1. 始终在设备上使用 thrust::device_vector 到 运行;

  2. 在编译时定义不同的宏到select要成为GPU的设备或CPU(OpenMP/TBB/CPP)。

更多信息在这里:

https://github.com/thrust/thrust/wiki/Device-Backends

是的,有可能,和你描述的差不多。

这是一个完整的示例:

$ cat t1205.cu
#include <thrust/execution_policy.h>
#include <thrust/for_each.h>
#include <thrust/device_vector.h>
#include <thrust/sequence.h>
#include <iostream>
#include <vector>

struct AddOne {
    int *numbers;
     template <typename T>
     __host__ __device__
     void operator()(T &i) {
         numbers[i] = numbers[i] + 1;
     }
};

int main(){

  AddOne addOneFunctor;
  std::vector<int> idx(100), numbers(100);
  addOneFunctor.numbers = thrust::raw_pointer_cast(numbers.data());
  thrust::sequence(idx.begin(), idx.end(), 0);
  thrust::for_each(thrust::cpp::par, idx.begin(), idx.end(), addOneFunctor);
  for (int i = 0; i < 5; i++)
    std::cout << numbers[i] << ",";
  std::cout << std::endl;

  thrust::device_vector<int> didx(100), dnumbers(100);
  addOneFunctor.numbers = thrust::raw_pointer_cast(dnumbers.data());
  thrust::sequence(didx.begin(), didx.end(), 0);
  thrust::for_each(thrust::device, didx.begin(), didx.end(), addOneFunctor);
  for (int i = 0; i < 5; i++)
    std::cout << dnumbers[i] << ",";
  std::cout << std::endl;
}
$ nvcc -o t1205 t1205.cu
$ ./t1205
1,1,1,1,1,
1,1,1,1,1,
$

请注意算法是 thrust::sequence 而不是 thrust::seq

使用 CUDA 8RC

作为@m.s。指出,没有必要在上述代码的算法上明确使用执行策略——您可以删除它们,它的工作方式相同。然而,正式使用执行策略允许将上面的示例扩展到您不使用容器,而是使用普通主机和设备数据的情况,因此它可能仍然具有一定的价值,具体取决于您的总体目标。