我写过一个必须处理大量数据的C ++应用程序。使用OpenMP我很好地并行化了处理阶段,并且令人尴尬地发现输出写入现在是瓶颈。我决定在那里使用parallel for
,因为我输出项目的顺序无关紧要;它们只需要输出为连贯的块。
下面是输出代码的简化版本,显示除“两个自定义迭代器”中的两个自定义迭代器之外的所有变量。我的问题是:这是解决这个问题的正确和最佳方法吗?我读到了barrier
pragma,我需要它吗?
long i, n = nrows();
#pragma omp parallel for
for (i=0; i<n; i++) {
std::vector<MyData> related;
for (size_t j=0; j < data[i].size(); j++)
related.push_back(data[i][j]);
sort(related.rbegin(), related.rend());
#pragma omp critical
{
std::cout << data[i].label << "\n";
for (size_t j=0; j<related.size(); j++)
std::cout << " " << related[j].label << "\n";
}
}
(我将此问题标记为c
,因为我认为OpenMP在C和C ++中非常相似。如果我错了,请纠正我。)
答案 0 :(得分:8)
解决输出争用的一种方法是将线程本地输出写入字符串流(可以并行完成),然后将内容推送到cout
(需要同步)。
这样的事情:
#pragma omp parallel for
for (i=0; i<n; i++) {
std::vector<MyData> related;
for (size_t j=0; j < data[i].size(); j++)
related.push_back(data[i][j]);
sort(related.rbegin(), related.rend());
std::stringstream buf;
buf << data[i].label << "\n";
for (size_t j=0; j<related.size(); j++)
buf << " " << related[j].label << "\n";
#pragma omp critical
std::cout << buf.rdbuf();
}
这提供了更细粒度的锁定,并且性能应相应提高。另一方面,这个仍然使用锁定。另一种方法是使用一个流缓冲区数组,每个线程一个,并在并行循环后按顺序>将它们推送到cout
。这样做的好处是可以避免代价高昂的锁定,并且无论如何都必须将cout
的输出序列化。
另一方面,您甚至可以尝试省略上述代码中的critical
部分。根据我的经验,这是有效的,因为底层流有自己的控制并发的方式。但我认为这种行为是严格的实现定义而不是可移植的。
答案 1 :(得分:2)
cout
争论仍然是一个问题。为什么不在一些线程本地存储中输出结果并将它们集中整理到所需位置,这意味着没有争用。例如,您可以让并行代码的每个目标线程写入单独的文件流或内存流,然后将它们连接起来,因为排序并不重要。或者从多个地方而不是一个地方对结果进行后处理 - 没有争用,只需要单次写入。