gpt4 book ai didi

c++ - 使用主机上CUDA内核中动态分配的数据

转载 作者:行者123 更新时间:2023-11-28 05:18:39 26 4
gpt4 key购买 nike

我正在尝试在管理一些内存的设备上建立一个容器类。
该内存是动态分配的,并在内核中的对象构建期间填充。
根据可以在内核中使用简单new []完成的文档(在Visual Studio 2012中将CUDA 8.0与计算能力5.0一起使用)。
之后,我想以主机代码访问容器内部的数据(例如用于测试所有值是否正确)。

DeviceContainer类的最低版本如下所示:

class DeviceContainer 
{
public:
__device__ DeviceContainer(unsigned int size);
__host__ __device__ ~DeviceContainer();

__host__ __device__ DeviceContainer(const DeviceContainer & other);
__host__ __device__ DeviceContainer & operator=(const DeviceContainer & other);

__host__ __device__ unsigned int getSize() const { return m_sizeData; }
__device__ int * getDataDevice() const { return mp_dev_data; }
__host__ int* getDataHost() const;

private:
int * mp_dev_data;
unsigned int m_sizeData;
};


__device__ DeviceContainer::DeviceContainer(unsigned int size) :
m_sizeData(size), mp_dev_data(nullptr)
{
mp_dev_data = new int[m_sizeData];

for(unsigned int i = 0; i < m_sizeData; ++i) {
mp_dev_data[i] = i;
}
}


__host__ __device__ DeviceContainer::DeviceContainer(const DeviceContainer & other) :
m_sizeData(other.m_sizeData)
{
#ifndef __CUDA_ARCH__
cudaSafeCall( cudaMalloc((void**)&mp_dev_data, m_sizeData * sizeof(int)) );
cudaSafeCall( cudaMemcpy(mp_dev_data, other.mp_dev_data, m_sizeData * sizeof(int), cudaMemcpyDeviceToDevice) );
#else
mp_dev_data = new int[m_sizeData];
memcpy(mp_dev_data, other.mp_dev_data, m_sizeData * sizeof(int));
#endif
}


__host__ __device__ DeviceContainer::~DeviceContainer()
{
#ifndef __CUDA_ARCH__
cudaSafeCall( cudaFree(mp_dev_data) );
#else
delete[] mp_dev_data;
#endif
mp_dev_data = nullptr;
}


__host__ __device__ DeviceContainer & DeviceContainer::operator=(const DeviceContainer & other)
{
m_sizeData = other.m_sizeData;

#ifndef __CUDA_ARCH__
cudaSafeCall( cudaMalloc((void**)&mp_dev_data, m_sizeData * sizeof(int)) );
cudaSafeCall( cudaMemcpy(mp_dev_data, other.mp_dev_data, m_sizeData * sizeof(int), cudaMemcpyDeviceToDevice) );
#else
mp_dev_data = new int[m_sizeData];
memcpy(mp_dev_data, other.mp_dev_data, m_sizeData * sizeof(int));
#endif

return *this;
}


__host__ int* DeviceContainer::getDataHost() const
{
int * pDataHost = new int[m_sizeData];
cudaSafeCall( cudaMemcpy(pDataHost, mp_dev_data, m_sizeData * sizeof(int), cudaMemcpyDeviceToHost) );
return pDataHost;
}


它仅管理数组 mp_dev_data
创建该数组并在构造期间将其填充为连续的值,这仅应在设备上可行。 (请注意,实际上容器的大小可能彼此不同。)

我想我需要提供一个复制构造函数和一个赋值运算符,因为我不知道其他任何方法来填充内核中的数组。 (请参阅下面的问题3。)
由于复制和删除也可以在主机上进行,因此 __CUDA_ARCH__用于确定我们要编译的执行路径。在主机上使用 cudaMemcpycudaFree,在设备上我们只能使用 memcpydelete[]

创建对象的内核非常简单:

__global__ void createContainer(DeviceContainer * pContainer, unsigned int numContainer, unsigned int containerSize)
{
unsigned int offset = blockIdx.x * blockDim.x + threadIdx.x;

if(offset < numContainer)
{
pContainer[offset] = DeviceContainer(containerSize);
}
}


范围内的一维网格中的每个线程都会创建一个容器对象。

然后,main函数为设备和主机上的容器(在本例中为90000)分配数组,调用内核并尝试使用对象:

void main()
{
const unsigned int numContainer = 90000;
const unsigned int containerSize = 5;

DeviceContainer * pDevContainer;
cudaSafeCall( cudaMalloc((void**)&pDevContainer, numContainer * sizeof(DeviceContainer)) );

dim3 blockSize(1024, 1, 1);
dim3 gridSize((numContainer + blockSize.x - 1)/blockSize.x , 1, 1);

createContainer<<<gridSize, blockSize>>>(pDevContainer, numContainer, containerSize);
cudaCheckError();

DeviceContainer * pHostContainer = (DeviceContainer *)malloc(numContainer * sizeof(DeviceContainer));
cudaSafeCall( cudaMemcpy(pHostContainer, pDevContainer, numContainer * sizeof(DeviceContainer), cudaMemcpyDeviceToHost) );

for(unsigned int i = 0; i < numContainer; ++i)
{
const DeviceContainer & dc = pHostContainer[i];

int * pData = dc.getDataHost();
for(unsigned int j = 0; j < dc.getSize(); ++j)
{
std::cout << pData[j];
}
std::cout << std::endl;
delete[] pData;
}

free(pHostContainer);
cudaSafeCall( cudaFree(pDevContainer) );
}


我必须在主机上使用 malloc进行数组创建,因为我不想为 DeviceContainer使用默认构造函数。
我尝试通过内部仅调用 getDataHost()cudaMemcpy访问容器内的数据。

cudaSafeCallcudaCheckError是简单的宏,用于评估函数oder返回的 cudaError主动轮询最后一个错误。为了完整性:

#define cudaSafeCall(error) __cudaSafeCall(error, __FILE__, __LINE__)
#define cudaCheckError() __cudaCheckError(__FILE__, __LINE__)

inline void __cudaSafeCall(cudaError error, const char *file, const int line)
{
if (error != cudaSuccess)
{
std::cerr << "cudaSafeCall() returned:" << std::endl;
std::cerr << "\tFile: " << file << ",\nLine: " << line << " - CudaError " << error << ":" << std::endl;
std::cerr << "\t" << cudaGetErrorString(error) << std::endl;

system("PAUSE");
exit( -1 );
}
}


inline void __cudaCheckError(const char *file, const int line)
{
cudaError error = cudaDeviceSynchronize();
if (error != cudaSuccess)
{
std::cerr << "cudaCheckError() returned:" << std::endl;
std::cerr << "\tFile: " << file << ",\tLine: " << line << " - CudaError " << error << ":" << std::endl;
std::cerr << "\t" << cudaGetErrorString(error) << std::endl;

system("PAUSE");
exit( -1 );
}
}


这段代码有3个问题:


如果按此处介绍的那样执行,我将收到内核的“未指定启动失败”。 Nsight调试器使我在 mp_dev_data = new int[m_sizeData];行上停止(无论在构造函数中还是在赋值运算符中),并在全局内存上报告了一些访问冲突。违反次数似乎在4到11之间是随机的,它们发生在非连续线程中,但始终在网格的上端附近(框85和86)。
如果我将 numContainer减小到10,则内核运行平稳,但是,即使 cudaMamcpy不为0, getDataHost()中的 mp_dev_data也会失败,并带有无效的参数错误。(我怀疑赋值有误,并且内存已被另一个对象删除。)
即使我想知道如何通过适当的内存管理正确地实现 DeviceContainer,对我而言,使其成为不可复制和不可分配也就足够了。但是,我不知道如何在内核中正确填充容器数组。也许像

DeviceContainer dc(5);
memcpy(&pContainer[offset], &dc, sizeof(DeviceContainer));


这将导致在析构函数中删除 mp_dev_data的问题。我将需要手动管理感觉很脏的内存删除。


我还尝试在内核代码中使用 mallocfree而不是 newdelete,但是结果是相同的。

很抱歉,我无法以简短的方式提出问题。

TL; DR:如何实现在内核中动态分配内存并且也可以在主机代码中使用的类?如何在内核中使用无法复制或分配的对象初始化数组?

任何帮助表示赞赏。谢谢。

最佳答案

显然答案是:我试图做的事或多或少是不可能的。
在内核中用newmalloc分配的内存不是放在全局内存中,而是放在主机无法访问的特殊堆内存中。

访问主机上所有内存的唯一选择是,首先在全局内存中分配一个足以容纳堆中所有元素的数组,然后编写一个将所有元​​素从堆复制到全局内存的内核。

访问冲突是由有限的堆大小引起的(可由cudaDeviceSetLimit(cudaLimitMallocHeapSize, size_t size)更改)。

关于c++ - 使用主机上CUDA内核中动态分配的数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42000339/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com