OpenMP:共享同一算法的单线程和多线程实现
Posted
技术标签:
【中文标题】OpenMP:共享同一算法的单线程和多线程实现【英文标题】:OpenMP: Share single-threaded and multi-threaded implementations of the same algorithm 【发布时间】:2019-08-29 04:40:04 【问题描述】:我在一个代码库中工作,其中几个算法被实现了两次:一次在正确的位置使用#pragma omp parallel
,一次没有。这些函数被命名为AlgorithmMT()
和AlgorithmST()
。
简化示例:
/// Multi-threaded algorithm
std::vector<double>
AlgorithmMT(int n)
std::vector<double> result(n);
std::itoa(result.begin(), result.end(), 1.0);
#pragma omp parallel for
for (int i = 0; i < n; ++i)
result[i] = i / result[i];
return result;
/// Single-threaded algorithm
std::vector<double>
AlgorithmST(int n)
std::vector<double> result(n);
std::itoa(result.begin(), result.end(), 1.0);
// NOTE: there is no #pragma here
for (int i = 0; i < n; ++i)
result[i] = i / result[i];
return result;
假设我需要保留两个单独的函数(不能更改更高级别的代码),并且应该允许用户在运行时在它们之间进行选择,我怎样才能让这两个函数共享一个共同的实现?
我意识到该算法有点荒谬,并且可以在没有读取依赖于循环内的result
的情况下实现。请假设这是算法所需的结构。 :)
【问题讨论】:
【参考方案1】:一种简洁的方法是使用parallel
构造的if
子句,例如:
bool is_parallel = ...;
#pragma omp parallel for if (is_parallel)
这是在运行时评估的,并且每个定义具有产生单个线程的相同效果。
这种运行时区别与省略pragma
并不完全相同,编译器可能会以不同方式优化代码。虽然我不会太担心,但你应该观察你的表现。只需在没有-fopenmp
的情况下编译应用程序,并将性能与动态禁用的并行性进行比较。 如果存在差异,您可能不得不求助于冗余代码或以某种方式帮助编译器。请注意,性能可能因编译器而异。
【讨论】:
这看起来很棒。我试试看!【参考方案2】:您可以使用 OpenMP 运行时 API 中的 omp_set_num_threads
在并行部分之前将线程计数限制为 1,然后在该部分之后将其恢复。
警告:如果已经有其他并行线程在运行,那么omp_set_num_threads
也会影响那里的并行部分。
【讨论】:
【参考方案3】:因为#pragma
是预处理器,所以你不能做很多事情。
你可以用你的函数创建algorithm.cpp.part
,但用%parallel%
代替#pragma omp for
,然后在编译时,在makefile中用类似这样的东西替换文本:
sed '/%parallel%/c\#pragma omp parallel' algorithm.cpp.part > algorithm_mt.cpp
sed '/%parallel%/c\ ' algorithm.cpp.part > algorithm_st.cpp
如果你有很多这样的函数,它可以通过良好的 makefile 规则进行相对较好的扩展。
或者,如果您正在为 Windows 编译,您可以使用并发运行时,它会避免使用 #pragma
,这在您的情况下可能很有用。
(这个答案不要太苛刻,我在手机上写这个)
【讨论】:
您可以在运行时更改最大线程数,有效地使代码单线程。 但如果多个线程需要不同的设置,可能会导致问题。无论如何,我们的两个答案都是 hack,真的可能导致更大的问题。以上是关于OpenMP:共享同一算法的单线程和多线程实现的主要内容,如果未能解决你的问题,请参考以下文章