我试图在GPU上运行Brandes算法(基本上是带有一些额外操作和数据结构的bfs),并且我为每个线程分配一个顶点来运行brandes。我面临的问题是在我的代码中
我需要存储在bfs期间访问的每个顶点的父代
。在CPU实现中,只要创建一个向量映射并在我找到一个新的父对象(从技术上讲是一个动态扩展的数组)时调用push_back,就很容易实现。我不知道如何在CUDA中做到这一点。
以下是我需要的功能的示例代码:
vector<int> distance; //Initialized to 0
vector<int> paths; //Initialized to 0
vector<bool> visited; //Initialized to false
map <int, vector<int> > parents; //Parent vector of each key is empty
queue<int> q;
// Running bfs from vertex
q.push(vertex);
while(!q.empty())
{
int source = q.front();
q.pop();
for(auto neighbour : adjacency_list[source])
{
if(!visited[neighbour])
{
visited[neighbour] = true;
q.push(neighbour);
distance[neighbour] = distance[source] + 1;
}
if(distance[neighbour] == distance[source] + 1)
{
paths[neighbour] += paths[source];
parents[neighbour].push_back(source);
}
}
}
{
// Use data accumulated above for calculations
....
}
这是我在设备代码中实现时遇到的问题(功能)
父母[邻居] .push_back(源);
我的印象:
我可以为每个顶点过度分配(最大图的度数)父级列表,但这将使我浪费大量未使用的内存
将父级关系存储为大小为2 * Edges的数组中的边,但是我需要将顶点的所有父级一起存储(连续存储或存储在同一容器中),这在该实现中是不可能的
我知道gpu堆内存,但是想不出一种方法来利用它供我使用
最坏的情况:我首先运行bfs来找到否。每个顶点的父代,然后为每个顶点分配适当的内存,然后再次运行brandes。
答案 0 :(得分:0)
我认为您的印象1可以用here(每线程堆栈,已预先分配)大致实现。它有您提到的与过度分配有关的问题。在较新的GPU中,内存通常为几GB(或更多),因此,如果总内存不是问题,那么对过度分配的关注可能不是很严重。
我认为您的印象2可以用here(设备范围的线程安全矢量push_back)大致实现。它具有您提到的问题,与结果向量中结果的排序不足有关。收集操作完成后,可以通过排序操作解决这些问题。
(4。听起来您可能已经对如何做“最坏情况”的印象4有了想法。)
malloc
使用按需分配,或者new
。这样的内核内存分配非常缓慢,并且并非没有其自身的问题(例如,您可能必须保留额外的堆空间,内核分配的堆内存无法参与向主机的传输,小的分配可能会导致内存效率低下)用法),但是如果没有更多有关问题范围的信息,实际上是没有办法告诉您哪种方法最好。如果在遍历图形时跟踪父节点是相对少见的操作,则动态分配方法可能不是问题。这是一个如何创建简单向量(每个线程)的示例:
$ cat t376.cu
#include <iostream>
#include <cstdio>
#include <assert.h>
template <typename T>
class cu_vec{ // simple implementation of per-thread "vector"
const size_t alloc_block_size = 4096; // tuning parameter
T *my_ptr;
size_t n_items;
size_t alloc_blocks;
public:
__host__ __device__
cu_vec(){
assert(sizeof(T) <= alloc_block_size);
n_items = 0;
my_ptr = (T *)new char[alloc_block_size];
assert(my_ptr != NULL);
alloc_blocks = 1;}
__host__ __device__
cu_vec(size_t sz){
assert(sizeof(T) <= alloc_block_size);
n_items = sz;
alloc_blocks = (n_items*sizeof(T)+alloc_block_size-1)/alloc_block_size;
my_ptr = (T *)new char[alloc_blocks*alloc_block_size];
assert(my_ptr != NULL);
memset(my_ptr, 0, alloc_blocks*alloc_block_size);}
__host__ __device__
~cu_vec(){
if (my_ptr != NULL) delete[] my_ptr;
}
__host__ __device__
void push_back(T const &item){ // first test if we can just store new item
if ((n_items+1)*sizeof(T) > alloc_blocks*alloc_block_size){
T *temp = (T *)new char[(alloc_blocks+1)*alloc_block_size];
assert(temp != NULL);
memcpy(temp, my_ptr, alloc_blocks*alloc_block_size);
delete[] my_ptr;
my_ptr = temp;
alloc_blocks++;}
my_ptr[n_items] = item;
n_items++;}
__host__ __device__
size_t size(){
return n_items;}
__host__ __device__
void clear(){
n_items = 0;}
__host__ __device__
T& operator[](size_t idx){
assert(idx < n_items);
return my_ptr[idx];}
__host__ __device__
T& pop_back(){
if (n_items > 0){
n_items--;}
return my_ptr[n_items];}
__host__ __device__
T* data(){
return my_ptr;}
__host__ __device__
size_t storage_ratio(){
return alloc_block_size/sizeof(T);}
};
struct ss
{
unsigned x;
float y;
};
__global__ void test(){
cu_vec<ss> my_vec;
ss temp = {threadIdx.x, 2.0f};
my_vec.push_back(temp);
assert(my_vec.size() == 1);
assert(my_vec.storage_ratio() >= 1);
ss temp2 = my_vec[0];
printf("threadIdx.x: %u, ss.x: %u, ss.y: %f\n", threadIdx.x, temp2.x, temp2.y);
temp.y = 3.0f;
my_vec[0].x = temp.x;
my_vec[0].y = temp.y;
ss temp3 = my_vec.pop_back();
printf("threadIdx.x: %u, ss.x: %u, ss.y: %f\n", threadIdx.x, temp3.x, temp3.y);
my_vec.clear();
temp.x = 0;
for (int i = 0; i < 10000; i++){
my_vec.push_back(temp);
temp.x++;}
temp.x--;
for (int i = 0; i < 10000; i++) {
assert(my_vec.pop_back().x == temp.x);
temp.x--;}
cu_vec<ss> my_vec2(2);
assert(my_vec2[1].x == 0);
assert(my_vec2[1].y == 0.0f);
}
int main(){
//default heap space is 8MB, if needed reserve more with:
cudaDeviceSetLimit(cudaLimitMallocHeapSize, (1048576*32));
test<<<1, 4>>>();
cudaDeviceSynchronize();
}
$ nvcc -std=c++11 -o t376 t376.cu
$ cuda-memcheck ./t376
========= CUDA-MEMCHECK
threadIdx.x: 0, ss.x: 0, ss.y: 2.000000
threadIdx.x: 1, ss.x: 1, ss.y: 2.000000
threadIdx.x: 2, ss.x: 2, ss.y: 2.000000
threadIdx.x: 3, ss.x: 3, ss.y: 2.000000
threadIdx.x: 0, ss.x: 0, ss.y: 3.000000
threadIdx.x: 1, ss.x: 1, ss.y: 3.000000
threadIdx.x: 2, ss.x: 2, ss.y: 3.000000
threadIdx.x: 3, ss.x: 3, ss.y: 3.000000
========= ERROR SUMMARY: 0 errors
$
除了您在此处看到的代码外,尚未对代码进行测试。