我试图用CUDA加速一些代码,并且在尽可能保持代码可读性/可维护性的限制下。
我找到并并行化了一个隐藏在几个函数/循环中的函数。这个函数占处理时间的〜98%,但并没有单独利用足够的并行性来实现(大约几个块的顺序......)。同时执行时代码更快。但是,结果我被迫维护一个很大的堆栈对象列表,我必须迭代几次,请参阅下面的代码:
void do_work(int i, ...) {
// computationally expensive stuff...
}
void prereq_stuff(int i) {
int foo;
double bar;
// lots of big divergent control structures...
do_work(i); // maybe arrive here..
// output and what not that needs foo/bar...
}
int main() {
for (int i = 0; i < BIG_NUMBER; i++) {
prereq_stuff(i);
}
return 0;
}
已经变成......
// a struct that contains all the stack data..
struct StackMem {
int foo;
double bar;
};
void do_work_on_gpu(List<StackMem> contexts) {
// launch a kernel to handle to expensive stuff..
}
void prereq_stuff(StackMem* context, int i) {
// maybe queue up data for do_work_on_gpu()...
}
void cleanup_stuff(StackMem* context, int i) {
// output and what not that needs foo/bar...
}
int main() {
List<StackMem> contexts; // some container of stack objects
for (int i = 0; i < BIG_NUMBER; i++) {
StackMem* context = contexts.add();
prereq_stuff(context, i);
}
do_work_on_gpu(contexts); // calls the CUDA kernel
for (int i = 0; i < contexts.size(); i++) {
cleanup_stuff(contexts.get(i), i);
}
return 0;
}
我可以在这里使用某种设计构造/模式吗?或者这很简单,因为所有数据都可以同时调用do_work()?
答案 0 :(得分:3)
以下是我将如何处理这种情况:
您的上下文数据结构将是
struct { int* foos; double* bars; } contexts;
或
struct context_t { int foos; double bar; };
struct context_t* contexts;
(或类似的东西,但更高级,如果你知道那是什么就可以使用gsl::span_t<context_t>
)
您将为所有上下文预先分配内存,最好使用cudaMallocHost()
固定内存(这就是为什么我没有建议std::vector
)。
不要在每次调用prereq_stuff()
时创建上下文,只需将其传递给特定上下文或索引和上下文数组的迭代器,以便它不会将其上下文放入记忆中有一些任意的地方。
使用单个cudaMemcpy()
传递上下文数组。
每个线程,块或其他任何使用其ID来决定使用哪个上下文。
注意合并读取,因为上下文是12个字节,而NVIDIA GPU上的内存读取是以32字节(通过纹理缓存/ __ldg()
)或128字节为单位。
PS - 无论你最终实现什么,请确保并profile你的代码,并查看计算和I / O时间 - 当你花费CPU时,看看你有什么潜力改善。