在Thrust中使用函数内部的多态函子

时间:2014-04-30 15:17:08

标签: c++ cuda thrust

我需要一个函数来计算GPU上几个变量的一些“数学”函数。我决定使用Thrust及其zip_iterator将变量打包到元组中,并将我的数学函数实现为函数for_each。但是我希望有一个可以计算不同“数学”功能的通用功能。所以,我需要在函数中传递这个函子。

我认为,要完成这项任务,我应该实现一些具有不同版本operator()(Tuple t)的仿函数的简单层次结构(使用唯一的基类)。例如,仿函数可以是这样的:

struct Func {
    template <typename Tuple>
    __host__ __device__
    void operator()(Tuple arg) {
        thrust::get<1>(arg) = 0;
    }
};

strust f1 : public Func {
   ...
};

strust f2 : public Func {
   ...
};

问题是如何正确地将所需的函子传递给函数,以及如何定义这样的函数?这个功能可以是:

thrust::host_vector evaluateFunction(Func func, thrust::host_vector point) {
    thrust::host_vector result(point.size());

    thrust::for_each(thrust::make_zip_iterator(thrust::make_tuple(point.begin(), result.begin())),
    thrust::make_zip_iterator(thrust::make_tuple(point.end(), result.end())),
                 func);
    return result;
}

但有了这样的定义,我无法将f1f2传递给它。我该如何正确定义?

1 个答案:

答案 0 :(得分:1)

似乎您希望将用户定义或用户可选择的函数传递给推力,以便在仿函数中进行评估。基于此,我认为一个可能的答案与发布的here非常相似,但我会做一些其他的评论。

  1. 我认为你实际上并不是在问这个问题,但我认为polymorphism在主机类中表达,最终传递给设备以供thrust::device_vector使用的对象基本上是不可能,因为It is not allowed to pass as an argument to a __global__ function an object of a class with virtual functions.虽然它并不完全明显,但如果我在主机上创建了一个合适的对象数组,然后尝试在thrust::device_vector中多态地使用它们,那么这实际上就是我要做的。至少,我无法在丛林中战斗。当然,您可以在设备代码中使用多态,假设在设备上正确创建了对象,以便可以正确分配其虚拟功能表。 This question and comments也可能是有意义的。如果您想查看在设备对象上使用多态的示例,请查看我的答案here。它演示了使用对象本身来定义您希望对其执行的功能的想法。 (虽然,我认为这不是你的想法。)
  2. 根据您编辑的问题,您似乎想要传递函数的地址,以便在仿函数中使用。这也是(至少以直截了当的方式)使用CUDA是不可能的,因为it is not allowed to take the address of a __device__ function in host code.这种限制可以通过调用“初始化内核”来解决,该初始化内核填充了设备函数指针的使用表以供使用在设备代码中,但我认为净效应并不比我在下面3中提出的要好。
  3. 您可以传递函数的用户可选索引,以便在设备代码中使用。
  4. 这是一个展示最后一个想法的例子:

    #include <thrust/device_vector.h>
    #include <thrust/sequence.h>
    #include <thrust/copy.h>
    #include <thrust/for_each.h>
    #include <thrust/iterator/zip_iterator.h>
    #include <iostream>
    #include <math.h>
    
    
    __host__ __device__ float f1(float x)
    {
      return sinf(x);
    }
    
    __host__ __device__ float f2(float x)
    {
      return cosf(x);
    }
    
    
    
    struct select_functor
    {
      unsigned fn;
    
      select_functor(unsigned _fn) : fn(_fn) {};
      template <typename Tuple>
      __host__ __device__
      void operator()(const Tuple &t) {
        if (fn == 1) thrust::get<1>(t)  = f1(thrust::get<0>(t));
        else if (fn == 2) thrust::get<1>(t)  = f2(thrust::get<0>(t));
        else thrust::get<1>(t) = 0;
      }
    };
    
    
    int main(void)
    {
      unsigned ufn = 1;
      const unsigned N = 8;
      thrust::device_vector<float> data(N), result(N);
      // initilaize to some values
      thrust::sequence(data.begin(), data.end(),  0.0f, (float)(6.283/(float)N));
      std::cout<< "x: " << std::endl;
      thrust::copy(data.begin(), data.end(), std::ostream_iterator<float>(std::cout, " "));
      thrust::for_each(thrust::make_zip_iterator(thrust::make_tuple(data.begin(), result.begin())),thrust::make_zip_iterator(thrust::make_tuple(data.end(),result.end())),select_functor(ufn));
      std::cout<< std::endl << "sin(x): " << std::endl;
      thrust::copy(result.begin(), result.end(), std::ostream_iterator<float>(std::cout, " "));
      ufn = 2;
      thrust::for_each(thrust::make_zip_iterator(thrust::make_tuple(data.begin(), result.begin())),thrust::make_zip_iterator(thrust::make_tuple(data.end(),result.end())),select_functor(ufn));
      std::cout<< std::endl << "cos(x): " << std::endl;
      thrust::copy(result.begin(), result.end(), std::ostream_iterator<float>(std::cout, " "));
      std::cout<< std::endl;
      return 0;
    }
    
相关问题