在保存堆栈数据以利用并行性时,最小化代码复杂性的最佳方法是什么?

时间:2017-07-17 21:45:10

标签: design-patterns parallel-processing cuda code-readability

我试图用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()?

1 个答案:

答案 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时,看看你有什么潜力改善。