【问题标题】:How to implement device side CUDA virtual functions?如何实现设备端CUDA虚拟功能?
【发布时间】:2014-11-08 02:02:29
【问题描述】:

我看到 CUDA 不允许将具有虚函数的类传递给内核函数。是否有任何解决此限制的方法?

我真的很希望能够在内核函数中使用多态性。

谢谢!

【问题讨论】:

  • 您可以在 CUDA 内核函数中使用多态性。只需在设备上创建对象即可。这通常应该不难做到,即使您需要使用来自主机的数据初始化这些对象。我提供了一个答案here,它用推力演示了这个概念,当然它也可以在普通的 CUDA 代码中工作。
  • @Robert Crovella 看来这个问题的根源是虚拟功能表的地址特定于设备。这在对象在设备之间移动的多 GPU 应用程序中会产生什么影响?前任。我在设备 0 上实例化一个多态类,然后将实例 memcpy 到设备 1(其中也有相同类的实例)。该对象会破坏 memcopied 对象,还是会无缝使用设备 1 的虚函数表?
  • 我不希望它起作用。虚函数表基本上是一组指针。这些指针(地址)只会对它们要被调用的设备是准确的。事实上,我希望 UVA 几乎可以保证它不会起作用。您也许可以让它在非 UVA 环境中工作,但我不会指望它。但是我只是在这里推测。我自己没试过。

标签: cuda virtual-functions


【解决方案1】:

Robert Crovella 评论中最重要的部分是:

只需在设备上创建对象即可。

因此,请记住这一点,我处理的情况是,我有一个抽象的 class Function,然后它的一些实现封装了不同的函数及其评估。这是我的代码的简化版本,我是如何在我的情况下实现多态性的,但我并不是说它不能做得更好......它希望能帮助你理解这个想法:

class Function
{
public:
    __device__ Function() {}
    __device__ virtual ~Function() {}
    __device__ virtual void Evaluate(const real* __restrict__ positions, real* fitnesses, const SIZE_TYPE particlesCount) const = 0;
};

class FunctionRsj : public Function
{
private:
    SIZE_TYPE m_DimensionsCount;
    SIZE_TYPE m_PointsCount;
    real* m_Y;
    real* m_X;
public:
    __device__ FunctionRsj(const SIZE_TYPE dimensionsCount, const SIZE_TYPE pointsCount, real* configFileData)
        : m_DimensionsCount(dimensionsCount),
            m_PointsCount(pointsCount),
            m_Y(configFileData),
            m_X(configFileData + pointsCount) {}

    __device__ ~FunctionRsj()
    {
        // m_Y points to the beginning of the config
        // file data, use it for destruction as this 
        // object took ownership of configFilDeata.
        delete[] m_Y;
    }

    __device__ void Evaluate(const real* __restrict__ positions, real* fitnesses, const SIZE_TYPE particlesCount) const
    {
        // Implement evaluation of FunctionRsj here.
    }
};

__global__ void evaluate_fitnesses(
    const real* __restrict__ positions,
    real* fitnesses,
    Function const* const* __restrict__ function,
    const SIZE_TYPE particlesCount)
{
    // This whole kernel is just a proxy as kernels
    // cannot be member functions.
    (*function)->Evaluate(positions, fitnesses, particlesCount);
}

__global__ void create_function(
    Function** function,
    SIZE_TYPE dimensionsCount,
    SIZE_TYPE pointsCount,
    real* configFileData)
{
    // It is necessary to create object representing a function
    // directly in global memory of the GPU device for virtual
    // functions to work correctly, i.e. virtual function table
    // HAS to be on GPU as well.
    if (threadIdx.x == 0 && blockIdx.x == 0)
    {
        (*function) = new FunctionRsj(dimensionsCount, pointsCount, configFileData);
    }
}

__global__ void delete_function(Function** function)
{
    delete *function;
}

int main()
{
    // Lets just assume d_FunctionConfigData, d_Positions,
    // d_Fitnesses are arrays allocated on GPU already ...

    // Create function.
    Function** d_Function;
    cudaMalloc(&d_Function, sizeof(Function**));
    create_function<<<1, 1>>>(d_Function, 10, 10, d_FunctionConfigData);

    // Evaluate using proxy kernel.
    evaluate_fitnesses<<<
        m_Configuration.GetEvaluationGridSize(),
        m_Configuration.GetEvaluationBlockSize(),
        m_Configuration.GetEvaluationSharedMemorySize()>>>(
        d_Positions,
        d_Fitnesses,
        d_Function,
        m_Configuration.GetParticlesCount());

    // Delete function object on GPU.
    delete_function<<<1, 1>>>(d_Function);
}

【讨论】:

  • 这正是我想要的。谢谢!!一个后续问题:在内核或设备函数中调用 newmalloc 是否会对性能造成巨大影响?
  • @Acerebral 就我而言,我没有经历过任何重大开销,总而言之,我无法想象更好的方法来解决我的问题,所以即使mallocnew 会损害性能,我不会太在意。我与没有抽象类的简单方法进行了比较,在 CPU 上创建函数,然后使用cudaMemcpy,但性能几乎相同。我想只在必要时实施解决方案并进行优化。
  • 构造函数和析构函数是否也必须以__device__关键字为前缀?如果是这样,为什么有必要。
猜你喜欢
  • 1970-01-01
  • 2011-01-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-08
  • 2015-02-01
  • 2018-07-19
  • 2014-11-10
相关资源
最近更新 更多