multidimensional-array - 为什么推力::device_vector 似乎没有机会保存指向其他 device_vectors 的原始指针?
问题描述
我有一个问题,我发现了很多线程,但没有一个明确回答我的问题。我正在尝试使用推力在 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();
}
解决方案
你真的应该提供一个最小的、完整的、可验证/可重现的例子;你的既不是最小的,也不是完整的,也不是可验证的。
但是,我将回答您的附带问题:
我知道我不能拥有
device_vector
s ofdevice_vector
s,无论出于何种根本原因(欢迎解释)
虽然 adevice_vector
涉及 GPU 上的一堆数据,但它是主机端数据结构 - 否则您将无法在主机端代码中使用它。在主机端,它所拥有的应该是:容量、元素大小、指向实际数据的设备端指针,也许还有更多信息。这类似于std::vector
变量如何引用堆上的数据,但是如果您在本地创建变量,我上面提到的字段将存在于堆栈中。
现在,位于主机内存中的设备向量的那些字段通常不能从设备端访问。在设备端代码中,您通常会使用指向device_vector
管理的设备端数据的原始指针。
另外,请注意,如果您有thrust::device_vector<T> v
,则每次使用都 operator[]
意味着一堆单独的 CUDA 调用来将数据复制到设备或从设备复制数据(除非在后台进行一些缓存)。所以你真的想避免在这种结构中使用方括号。
最后,请记住,指针追逐可能会成为性能杀手,尤其是在 GPU 上。您可能需要考虑在某种程度上按摩您的数据结构,以使其易于扁平化。
推荐阅读
- javascript - 窗口加载事件可以保证所有图像在 android webview 中完全呈现吗?
- excel - BeforeRightClick:单击随机单元格时选择全范围
- android - AndroidStudio 显示项目结构中不在 CMakeLists.txt 中的文件。怎么修?
- php - 来自 wordpress 的 REST API 调用中的服务不可用
- javascript - 网页抓取;javascript运行后如何渲染html?
- heroku - 网站不是从 WiFi 加载,而是来自移动数据 - 我的 IP 是否被阻止?
- angular - 如何在同一组件上切换标题?
- javascript - 如何在没有猫鼬的情况下使用 mongodb 获取 ObjectIds 数组的值
- android - 将 onSaveInstanceState 与 Viewmodel 一起使用时无法创建类的实例
- elasticsearch - 弹性搜索/休眠搜索:如何在 java 中使用查询 DSL API 编写 xor 运算符?