矢量填充跨OpenMP线程

时间:2016-03-31 14:51:15

标签: c++ vector openmp std

我有一个算法,其中一个目标是填充向量。出于性能考虑,算法的迭代遍布OpenMP线程。我想知道哪种方式可以提供更好/更安全的填充向量的方法。

请注意,向量的排序必须一致(即vec1的值n必须来自与vec2的值n相同的迭代。)

假设1:

std::vector<BasicType> vec1;
std::vector<BasicType> vec2;
#pragma opm parallel for
for(...)
{
    // Do some intensive stuff to compute val1 and val2
    // ...

    #pragma omp critical
    {
        vec1.push_back(val1);
        vec2.push_back(val2);
    }
}
// Then go on to work with vec1 and vec2...

假设2:

std::vector<BasicType> vec1;
std::vector<BasicType> vec2;
#pragma opm parallel
{
    std::vector<BasicType> vec1local;
    std::vector<BasicType> vec2local;
    #pragma omp for
    for(...)
    {
        // Do some intensive stuff to compute val1 and val2
        // ...

        vec1local.push_back(val1);
        vec2local.push_back(val2);
    }

    #pragma omp critical
    {
        // See note 1 below
        vec1.insert(vec1.end(), vec1local.begin(), vec1local.end());
        vec2.insert(vec2.end(), vec2local.begin(), vec2local.end());
    }
}
// Then go on to work with vec1 and vec2...

注1:这取自Best way to append vector to vector

注2:似乎val1和val2可以组合在某个对象中以保持一致性并且只能与一个向量一起工作,但是现在它对于算法的其余部分似乎是不切实际的。 / p>

注3:为了给出一个数量级,for循环包含在4个线程中分割的大约100次迭代。除了极少数例外,每次迭代都应该具有相同的工作负载(这会导致关键部分的问题在同一时间发生。)

注4:仅仅是为了记录,整个事情处理图像稳定,并在Tegra K1架构上运行。使用的编译器是gcc 4.8.4。

2 个答案:

答案 0 :(得分:2)

根据你的两个建议,我更喜欢第一个。它更简单,不需要额外的内存。但是,我会建议一个没有module.exports = ApplicationViewModel; // ES5 export default ApplicationViewModel; // ES6 部分的替代方案:

critical

请注意,由于缓存行窃取,这可能会导致一些性能问题。但是,我不担心,除非事实证明是实际性能分析验证的实际问题。那么解决方案就是:

  • 使用您的第二个解决方案。 (这需要内存和额外的后处理)
  • Align your vector并使用适当的块进行循环。 (这样每个线程都有本地缓存​​行)
  • 使用内部包含本地向量的数据结构,但向外部提供必要的全局向量操作。 (这可能是最好的解决方案。)

答案 1 :(得分:1)

我将假设您需要使用向量而不能使用数组(othrewise您的问题不是很有趣)。使用t = omp_get_num_threads(),您可以并行填充向量,然后将其合并到log2(t)个操作中,而不是t个操作(正如您现在所做的那样),就像这样

void reduce(std::vector<BasicType> *v1, std::vector<BasicType> *v2, int begin, int end) {
    if(end - begin == 1) return;
    int pivot = (begin+end)/2;
    #pragma omp task
    reduce(v, begin, pivot);
    #pragma omp task
    reduce(v, pivot, end);
    #pragma omp taskwait
    v1[begin].insert(v1[begin].end(), v1[pivot].begin(), v1[pivot].end());
    v2[begin].insert(v2[begin].end(), v2[pivot].begin(), v2[pivot].end());
}

std::vector<BasicType> v1, v2;
std::vector<BasicType> *v1p, *v2p;
#pragma omp parallel
{
    #pragma omp single
    {
        v1p = new std::vector<BasicType>[omp_get_num_threads()];
        v2p = new std::vector<BasicType>[omp_get_num_threads()];
    }
    #pragma omp for
    for(...) {
        // Do some intensive stuff to compute val1 and val2
        // ...
       v1p[omp_get_thread_num()].push_back(val1);
       v2p[omp_get_thread_num()].push_back(val2);
    }
    #pragma omp single
    reduce(v1p, v2p, 0, omp_get_num_threads());
}
v1 = v1p[0], v2 = v2p[0];
delete[] v1p;
delete[] v2p;

例如,对于8个线程,这将连接线程的向量

(0,1) (2,3) (4,5) (6,7)
(0,2) (4,6)
(0,4)

有关并行填充向量的更多信息,请参阅this。有关在log2(t)操作中合并线程的详细信息,请参阅this question的答案。