【发布时间】:2020-01-19 08:54:17
【问题描述】:
我有一个问题,我在其中找到了很多线程,但没有一个明确回答我的问题。 我正在尝试使用推力在 GPU 内核中创建一个多维数组。展平会很困难,因为所有尺寸都是不均匀的,我会上升到 4D。现在我知道我不能拥有 device_vectors 的 device_vectors,无论出于何种根本原因(欢迎解释),所以我尝试绕过原始指针。
我的理由是,原始指针指向 GPU 上的内存,否则我为什么能够从内核中访问它。所以从技术上讲,我应该能够拥有一个 device_vector,它包含原始指针,所有指针都应该可以从 GPU 中访问。这样我构造了以下代码:
thrust::device_vector<Vector3r*> d_fluidmodelParticlePositions(nModels);
thrust::device_vector<unsigned int***> d_allFluidNeighborParticles(nModels);
thrust::device_vector<unsigned int**> d_nFluidNeighborsCrossFluids(nModels);
for(unsigned int fluidModelIndex = 0; fluidModelIndex < nModels; fluidModelIndex++)
{
FluidModel *model = sim->getFluidModelFromPointSet(fluidModelIndex);
const unsigned int numParticles = model->numActiveParticles();
thrust::device_vector<Vector3r> d_neighborPositions(model->getPositions().begin(), model->getPositions().end());
d_fluidmodelParticlePositions[fluidModelIndex] = CudaHelper::GetPointer(d_neighborPositions);
thrust::device_vector<unsigned int**> d_fluidNeighborIndexes(nModels);
thrust::device_vector<unsigned int*> d_nNeighborsFluid(nModels);
for(unsigned int pid = 0; pid < nModels; pid++)
{
FluidModel *fm_neighbor = sim->getFluidModelFromPointSet(pid);
thrust::device_vector<unsigned int> d_nNeighbors(numParticles);
thrust::device_vector<unsigned int*> d_neighborIndexesArray(numParticles);
for(unsigned int i = 0; i < numParticles; i++)
{
const unsigned int nNeighbors = sim->numberOfNeighbors(fluidModelIndex, pid, i);
d_nNeighbors[i] = nNeighbors;
thrust::device_vector<unsigned int> d_neighborIndexes(nNeighbors);
for(unsigned int j = 0; j < nNeighbors; j++)
{
d_neighborIndexes[j] = sim->getNeighbor(fluidModelIndex, pid, i, j);
}
d_neighborIndexesArray[i] = CudaHelper::GetPointer(d_neighborIndexes);
}
d_fluidNeighborIndexes[pid] = CudaHelper::GetPointer(d_neighborIndexesArray);
d_nNeighborsFluid[pid] = CudaHelper::GetPointer(d_nNeighbors);
}
d_allFluidNeighborParticles[fluidModelIndex] = CudaHelper::GetPointer(d_fluidNeighborIndexes);
d_nFluidNeighborsCrossFluids[fluidModelIndex] = CudaHelper::GetPointer(d_nNeighborsFluid);
}
现在编译器不会抱怨了,但是从内核中访问例如 d_nFluidNeighborsCrossFluids 会起作用,但会返回错误的值。我像这样访问它(同样,从内核中):
d_nFluidNeighborsCrossFluids[iterator1][iterator2][iterator3];
// Note: out of bounds indexing guaranteed to not happen, indexing is definitely right
问题是,为什么它返回错误的值?我认为它背后的逻辑应该有效,因为我的索引是正确的,并且指针应该是内核中的有效地址。
感谢您抽出宝贵的时间,祝您有美好的一天。
编辑: 这是一个最小的可重现示例。出于某种原因,尽管与我的代码具有相同的结构,但这些值看起来是正确的,但是 cuda-memcheck 揭示了一些错误。取消注释两条注释行会导致我试图解决我的主要问题。这里的 cuda-memcheck 告诉我什么?
/* Part of this example has been taken from code of Robert Crovella
in a comment below */
#include <thrust/device_vector.h>
#include <stdio.h>
template<typename T>
static T* GetPointer(thrust::device_vector<T> &vector)
{
return thrust::raw_pointer_cast(vector.data());
}
__global__
void k(unsigned int ***nFluidNeighborsCrossFluids, unsigned int ****allFluidNeighborParticles){
const unsigned int i = blockIdx.x*blockDim.x + threadIdx.x;
if(i > 49)
return;
printf("i: %d nNeighbors: %d\n", i, nFluidNeighborsCrossFluids[0][0][i]);
//for(int j = 0; j < nFluidNeighborsCrossFluids[0][0][i]; j++)
// printf("i: %d j: %d neighbors: %d\n", i, j, allFluidNeighborParticles[0][0][i][j]);
}
int main(){
const unsigned int nModels = 2;
const int numParticles = 50;
thrust::device_vector<unsigned int**> d_nFluidNeighborsCrossFluids(nModels);
thrust::device_vector<unsigned int***> d_allFluidNeighborParticles(nModels);
for(unsigned int fluidModelIndex = 0; fluidModelIndex < nModels; fluidModelIndex++)
{
thrust::device_vector<unsigned int*> d_nNeighborsFluid(nModels);
thrust::device_vector<unsigned int**> d_fluidNeighborIndexes(nModels);
for(unsigned int pid = 0; pid < nModels; pid++)
{
thrust::device_vector<unsigned int> d_nNeighbors(numParticles);
thrust::device_vector<unsigned int*> d_neighborIndexesArray(numParticles);
for(unsigned int i = 0; i < numParticles; i++)
{
const unsigned int nNeighbors = i;
d_nNeighbors[i] = nNeighbors;
thrust::device_vector<unsigned int> d_neighborIndexes(nNeighbors);
for(unsigned int j = 0; j < nNeighbors; j++)
{
d_neighborIndexes[j] = i + j;
}
d_neighborIndexesArray[i] = GetPointer(d_neighborIndexes);
}
d_nNeighborsFluid[pid] = GetPointer(d_nNeighbors);
d_fluidNeighborIndexes[pid] = GetPointer(d_neighborIndexesArray);
}
d_nFluidNeighborsCrossFluids[fluidModelIndex] = GetPointer(d_nNeighborsFluid);
d_allFluidNeighborParticles[fluidModelIndex] = GetPointer(d_fluidNeighborIndexes);
}
k<<<256, 256>>>(GetPointer(d_nFluidNeighborsCrossFluids), GetPointer(d_allFluidNeighborParticles));
if (cudaGetLastError() != cudaSuccess)
printf("Sync kernel error: %s\n", cudaGetErrorString(cudaGetLastError()));
cudaDeviceSynchronize();
}
【问题讨论】:
-
设备向量可以保存指向设备数据的原始指针,无论该数据是否在另一个设备向量容器中。但是,由于您已将
d_nFluidNeighborsCrossFluids定义为设备向量,因此它在设备代码中不可用,您已经在问题中说明了这一点。如果您想在设备代码中使用它,请将指向d_nFluidNeighborsCrossFluids中数据的原始指针传递给您的设备代码,然后使用它。如果您想知道为什么您的特定代码不起作用,您应该提供minimal reproducible example,请参阅第 1 项here。 -
亲爱的罗伯特,感谢您的快速回复。在这个阶段很难快速复制示例,因为整个结构嵌入在一个大型项目中。我确实将原始指针传递给内核,然后在内核内部尝试通过 printf 再次访问,但这又给了我错误的值。内核在已编辑的问题中。
-
我给出的答案表明,一般概念是可行的。如果没有完整的示例可以使用,我不会尝试解释您的情况。在尝试创建这个最小但完整的示例的过程中,您很可能会自己发现问题。
-
在您尝试使用它们之前,您会让一堆设备向量超出范围。当您通过指针引用数据时,您最好确保指针指向的东西仍然有效。当设备向量超出范围时,底层数据将被释放。这会导致代码工作正常,但出现
cuda-memcheck错误。这基本上是缺乏对 C++ 编程的理解,而不是真正的 CUDA 特定问题。如果您在主机代码中使用std::vector执行此操作,也会出现同样的问题。 -
好的,我不确定你的意思。此设置中的打印值是正确的,那么它怎么可能是超出范围的问题呢?在没有 cuda-memcheck 的情况下运行程序似乎会按预期终止。
标签: multidimensional-array cuda thrust