CUDA/CUDA中的多态性和派生类

CUDA/CUDA中的多态性和派生类,cuda,polymorphism,thrust,Cuda,Polymorphism,Thrust,这是我关于堆栈溢出的第一个问题,这是一个相当长的问题。tl;dr版本是:如果我想让它同时存储不同类型的对象,DerivedClass1,DerivedClass2,那么如何使用推力::设备向量 我想利用CUDA推力的多态性。我正在编译一个-arch=sm_30GPU(GeForce GTX 670) 让我们看看下面的问题:假设镇上有80户人家。其中60人是已婚夫妇,20人是单亲家庭。因此,每个家庭都有不同数量的成员。现在是人口普查时间,家庭必须说明父母的年龄和孩子的数量。因此,政府构建了一系列家

这是我关于堆栈溢出的第一个问题,这是一个相当长的问题。tl;dr版本是:如果我想让它同时存储不同类型的对象,
DerivedClass1
DerivedClass2
,那么如何使用
推力::设备向量

我想利用CUDA推力的多态性。我正在编译一个
-arch=sm_30
GPU(GeForce GTX 670)

让我们看看下面的问题:假设镇上有80户人家。其中60人是已婚夫妇,20人是单亲家庭。因此,每个家庭都有不同数量的成员。现在是人口普查时间,家庭必须说明父母的年龄和孩子的数量。因此,政府构建了一系列
家庭
对象,即
推力::设备_矢量家庭小镇(80)
,这样家庭
家庭小镇[0]
家庭小镇[59]
的信息对应于已婚夫妇,其余(
家庭小镇[60]
家庭小镇[79]
)是单亲家庭

  • Family
    是基本类-家庭中的父母人数(单亲为1人,夫妻为2人)和他们的子女人数作为成员存储在此处
  • 单亲父母
    ,源自
    家庭
    ,包括一个新成员-单亲父母的年龄,
    未签名的父母
  • MarriedCouple
    ,也源于
    家庭
    ,但是,引入了两个新成员-父母的年龄,
    unsigned intage of parent1
    unsigned intage of parent2

    #include <iostream>
    #include <stdio.h>
    #include <thrust/device_vector.h>
    
    class Family
    {
    protected:
      unsigned int numParents;
      unsigned int numChildren;
    public:
      __host__ __device__ Family() {};
      __host__ __device__ Family(const unsigned int& nPars, const unsigned int& nChil) : numParents(nPars), numChildren(nChil) {};
      __host__ __device__ virtual ~Family() {};
    
      __host__ __device__ unsigned int showNumOfParents() {return numParents;}
      __host__ __device__ unsigned int showNumOfChildren() {return numChildren;}
    };
    
    class SingleParent : public Family
    {
    protected:
      unsigned int ageOfParent;
    public:
      __host__ __device__ SingleParent() {};
      __host__ __device__ SingleParent(const unsigned int& nChil, const unsigned int& age) : Family(1, nChil), ageOfParent(age) {};
    
      __host__ __device__ unsigned int showAgeOfParent() {return ageOfParent;}
    };
    
    class MarriedCouple : public Family
    {
    protected:
      unsigned int ageOfParent1;
      unsigned int ageOfParent2;
    public:
      __host__ __device__ MarriedCouple() {};
      __host__ __device__ MarriedCouple(const unsigned int& nChil, const unsigned int& age1, const unsigned int& age2) : Family(2, nChil), ageOfParent1(age1), ageOfParent2(age2) {};
    
      __host__ __device__ unsigned int showAgeOfParent1() {return ageOfParent1;}
      __host__ __device__ unsigned int showAgeOfParent2() {return ageOfParent2;}
    };
    
    我好像撞到墙了。我是否正确理解内存管理?(等)。我的对象是否正在设备上实例化和填充?我是否在泄露记忆,就像没有明天一样

    为了避免对象切片,我尝试了
    dynamic\u cast(basePointer)
    。这就是为什么我让我的
    家庭
    析构函数
    虚拟

    Family *pA = familiesInTown[2];
    MarriedCouple *pB = dynamic_cast<MarriedCouple*>(pA);
    
    Family*pA=familiesInTown[2];
    已婚夫妇*pB=动态演员阵容(pA);
    
    下面几行编译,但不幸的是,再次抛出segfault。CUDA Memcheck不会告诉我为什么

      std::cout << "Ages " << (pB -> showAgeOfParent1()) << ", " << (pB -> showAgeOfParent2()) << "\n";
    

    std::cout我不打算回答这个问题的所有问题,它太大了。话虽如此,以下是一些关于您发布的代码的观察结果,可能会有所帮助:

    • GPU端
      new
      操作符从私有运行时堆分配内存。从CUDA 6开始,主机端CUDA API无法访问该内存。您可以从内核和设备函数中访问内存,但主机无法访问该内存。因此,在推力装置函子中使用
      new
      ,是一种无法正常工作的破设计。这就是“指针向量”模型失败的原因
    • 推力的基本目的是允许典型STL算法的数据并行版本应用于POD类型。使用复杂的多态对象构建一个代码库,并试图通过推力容器和算法来填充这些代码库可能是可行的,但这不是推力设计的目的,我也不推荐这样做。如果你以意料之外的方式打破了推力,不要感到惊讶
    • 支持大量的C++特性,但是编译和对象模型比基于它们的C++ 98标准要简单得多。CUDA缺少几个关键的特性(例如RTTI),这使得复杂的多态对象设计在C++中是可行的。我的建议是节省使用C++的特性。仅仅因为你可以在CUDA做一些事情并不意味着你应该这样做。GPU是一种简单的体系结构,简单的数据结构和代码几乎总是比功能相似的复杂对象更具性能

    浏览了你发布的代码后,我的总体建议是回到绘图板。如果您想了解一些非常优雅的CUDA/C++设计,请花一些时间阅读和的代码库。它们都非常不同,但两者都有很多值得学习的地方(CUSP是建立在推力之上的,我想这会使它与您的用例更加相关)。

    我完全同意@talonmies的答案。(例如,我不知道推力已经用多态性进行了广泛的测试。)此外,我还没有完全解析您的代码。我发布这个答案是为了添加额外的信息,特别是我相信可以使某种程度的多态性与推力一起工作

    我要做的一个关键观察是,这意味着在主机上创建的多态对象不能传递给设备(通过推力,或者在普通的CUDAC++中)。(该限制的一个基础是对象中的虚拟函数表的要求,这在主机和设备之间必然不同,再加上在主机代码中直接获取设备函数的地址是非法的)

    但是,可以在设备代码中工作,包括推力设备功能

    下面的示例演示了这个想法,将我们自己限制为在设备上创建的对象,尽管我们当然可以使用主机数据初始化它们。我创建了两个类,
    Triangle
    Rectangle
    ,它们是从一个基类
    Polygon
    派生的,该基类包含一个虚拟函数
    区域
    <代码>三角形
    矩形
    从基类继承函数
    设置值
    ,但替换虚拟
    区域
    函数

    然后,我们可以多态地操作这些类的对象,如下所示:

    #include <iostream>
    #include <thrust/device_vector.h>
    #include <thrust/for_each.h>
    #include <thrust/sequence.h>
    #include <thrust/iterator/zip_iterator.h>
    #include <thrust/copy.h>
    #define N 4
    
    
    class Polygon {
      protected:
        int width, height;
      public:
      __host__ __device__  void set_values (int a, int b)
          { width=a; height=b; }
      __host__ __device__  virtual int area ()
          { return 0; }
    };
    
    class Rectangle: public Polygon {
      public:
      __host__ __device__  int area ()
          { return width * height; }
    };
    
    class Triangle: public Polygon {
      public:
      __host__ __device__   int area ()
          { return (width * height / 2); }
    };
    
    
    struct init_f {
      template <typename Tuple>
      __host__ __device__ void operator()(const Tuple &arg) {
        (thrust::get<0>(arg)).set_values(thrust::get<1>(arg), thrust::get<2>(arg));}
    };
    
    struct setup_f {
      template <typename Tuple>
      __host__ __device__ void operator()(const Tuple &arg) {
        if (thrust::get<0>(arg) == 0)
          thrust::get<1>(arg) = &(thrust::get<2>(arg));
        else
          thrust::get<1>(arg) = &(thrust::get<3>(arg));}
    };
    
    struct area_f {
      template <typename Tuple>
      __host__ __device__ void operator()(const Tuple &arg) {
        thrust::get<1>(arg) = (thrust::get<0>(arg))->area();}
    };
    
    
    int main () {
    
      thrust::device_vector<int>  widths(N);
      thrust::device_vector<int> heights(N);
      thrust::sequence( widths.begin(),  widths.end(), 2);
      thrust::sequence(heights.begin(), heights.end(), 3);
      thrust::device_vector<Rectangle> rects(N);
      thrust::device_vector<Triangle>  trgls(N);
      thrust::for_each(thrust::make_zip_iterator(thrust::make_tuple(rects.begin(), widths.begin(), heights.begin())), thrust::make_zip_iterator(thrust::make_tuple(rects.end(), widths.end(), heights.end())), init_f());
      thrust::for_each(thrust::make_zip_iterator(thrust::make_tuple(trgls.begin(), widths.begin(), heights.begin())), thrust::make_zip_iterator(thrust::make_tuple(trgls.end(), widths.end(), heights.end())), init_f());
      thrust::device_vector<Polygon *> polys(N);
      thrust::device_vector<int> selector(N);
      for (int i = 0; i<N; i++) selector[i] = i%2;
      thrust::for_each(thrust::make_zip_iterator(thrust::make_tuple(selector.begin(), polys.begin(), rects.begin(), trgls.begin())), thrust::make_zip_iterator(thrust::make_tuple(selector.end(), polys.end(), rects.end(), trgls.end())), setup_f());
      thrust::device_vector<int> areas(N);
      thrust::for_each(thrust::make_zip_iterator(thrust::make_tuple(polys.begin(), areas.begin())), thrust::make_zip_iterator(thrust::make_tuple(polys.end(), areas.end())), area_f());
      thrust::copy(areas.begin(), areas.end(), std::ostream_iterator<int>(std::cout, "\n"));
      return 0;
    }
    
    #包括
    #包括
    #包括
    #包括
    #包括
    #包括
    #定义n4
    类多边形{
    受保护的:
    int宽度、高度;
    公众:
    __主机设备无效设置值(int a,int b)
    {宽度=a;高度=b;}
    __主人_
    
      std::cout << "Ages " << (pB -> showAgeOfParent1()) << ", " << (pB -> showAgeOfParent2()) << "\n";
    
      MarriedCouple B = *pB;
      std::cout << "Ages " << B.showAgeOfParent1() << ", " << B.showAgeOfParent2() << "\n";
    
    #include <iostream>
    #include <thrust/device_vector.h>
    #include <thrust/for_each.h>
    #include <thrust/sequence.h>
    #include <thrust/iterator/zip_iterator.h>
    #include <thrust/copy.h>
    #define N 4
    
    
    class Polygon {
      protected:
        int width, height;
      public:
      __host__ __device__  void set_values (int a, int b)
          { width=a; height=b; }
      __host__ __device__  virtual int area ()
          { return 0; }
    };
    
    class Rectangle: public Polygon {
      public:
      __host__ __device__  int area ()
          { return width * height; }
    };
    
    class Triangle: public Polygon {
      public:
      __host__ __device__   int area ()
          { return (width * height / 2); }
    };
    
    
    struct init_f {
      template <typename Tuple>
      __host__ __device__ void operator()(const Tuple &arg) {
        (thrust::get<0>(arg)).set_values(thrust::get<1>(arg), thrust::get<2>(arg));}
    };
    
    struct setup_f {
      template <typename Tuple>
      __host__ __device__ void operator()(const Tuple &arg) {
        if (thrust::get<0>(arg) == 0)
          thrust::get<1>(arg) = &(thrust::get<2>(arg));
        else
          thrust::get<1>(arg) = &(thrust::get<3>(arg));}
    };
    
    struct area_f {
      template <typename Tuple>
      __host__ __device__ void operator()(const Tuple &arg) {
        thrust::get<1>(arg) = (thrust::get<0>(arg))->area();}
    };
    
    
    int main () {
    
      thrust::device_vector<int>  widths(N);
      thrust::device_vector<int> heights(N);
      thrust::sequence( widths.begin(),  widths.end(), 2);
      thrust::sequence(heights.begin(), heights.end(), 3);
      thrust::device_vector<Rectangle> rects(N);
      thrust::device_vector<Triangle>  trgls(N);
      thrust::for_each(thrust::make_zip_iterator(thrust::make_tuple(rects.begin(), widths.begin(), heights.begin())), thrust::make_zip_iterator(thrust::make_tuple(rects.end(), widths.end(), heights.end())), init_f());
      thrust::for_each(thrust::make_zip_iterator(thrust::make_tuple(trgls.begin(), widths.begin(), heights.begin())), thrust::make_zip_iterator(thrust::make_tuple(trgls.end(), widths.end(), heights.end())), init_f());
      thrust::device_vector<Polygon *> polys(N);
      thrust::device_vector<int> selector(N);
      for (int i = 0; i<N; i++) selector[i] = i%2;
      thrust::for_each(thrust::make_zip_iterator(thrust::make_tuple(selector.begin(), polys.begin(), rects.begin(), trgls.begin())), thrust::make_zip_iterator(thrust::make_tuple(selector.end(), polys.end(), rects.end(), trgls.end())), setup_f());
      thrust::device_vector<int> areas(N);
      thrust::for_each(thrust::make_zip_iterator(thrust::make_tuple(polys.begin(), areas.begin())), thrust::make_zip_iterator(thrust::make_tuple(polys.end(), areas.end())), area_f());
      thrust::copy(areas.begin(), areas.end(), std::ostream_iterator<int>(std::cout, "\n"));
      return 0;
    }