严正声明:本文系作者davidhopper原创,未经许可,不得转载。
2019年8月5日更新:
GCC 9.1.0可支持C++ 17标准的并行策略,本文方法已过时。
参考我的另一篇博客:《Ubuntu 16.04系统中使用GCC 9.1及Intel TBB库运行C++17 STL并行算法库》。
C++ 17标准中一个令人兴奋的特性是对STL库中的69个算法加入了执行策略(execution policies),允许在少量修改的情形下,对原有STL库算法实现并行计算,这对希望提高效率的开发者无疑是一个很大的福音。令人遗憾地是,目前主流C++编译器尚未加入对该特性的支持。
GCC编译器作为Linux系统中最主流的编译器,其最新版本GCC 7.3几乎完全实现了对C++ 17标准的支持(如下图所示),也可完成多数STL库算法的并行计算,但它不是以C++ 17标准所规范的基于执行策略来实现,而是以添加额外头文件和编译选项的方式来完成,希望GCC后续版本能尽快完成该特性的标准化。
下面是采用C++ 17标准执行策略方式实现并行计算的一个简单示例。该示例虽然符合C++ 17标准,但在我写作本文时,使用最新版本的GCC 7.3不能编译通过。
#include
#include
#include
#include
#include
using namespace std;
bool odd(int n) { return n % 2; }
int main() {
vector<int> d(500);
mt19937 gen;
uniform_int_distribution<int> dis(0, 100000);
auto rand_num([=]() mutable { return dis(gen); });
generate(execution::par, begin(d), end(d), rand_num);
sort(execution::par, begin(d), end(d));
reverse(execution::par, begin(d), end(d));
auto odds(count_if(execution::par, begin(d), end(d), odd));
cout << (100.0 * odds / d.size()) << "% of the numbers are odd.\n";
return 0;
}
在GCC编译器尚未支持C++ 17标准执行策略的情形下,我们以什么方式完成C++ STL库算法的并行计算呢?本文将以Ubuntu 16.04自带的GCC 5.4编译器对其进行阐述。
使用GCC编译器编译并行计算程序需要OpenMP库的支持,目前GCC编译器自带该库,无需自己下载,只需在编译程序时添加一个额外选项-fopenmp
即可,该选项会指示编译器链接并行计算库:libgomp
。
GCC编译器在某些硬件平台(例如sparc和x86)上默认不支持原子操作(atomic operations),要在这些硬件平台上编译并行计算程序,需显式指定额外的编译选项,如:-march=i686
、-march=native
或-mcpu=v9
,更多信息请查阅GCC编译器使用手册。
为使用libstdc++
并行模式,还需在编译程序时添加一个宏定义: -D_GLIBCXX_PARALLEL
. 该宏会指示编译器在可行的前提下,使用STL库算法的并行计算版本。
注意:并非所有的STL库算法在GCC编译器中都有对应的并行计算版本,下表给出了GCC编译器所支持的STL并行算法:
Algorithm | Header | Parallel algorithm | Parallel header |
---|---|---|---|
std::accumulate |
numeric |
__gnu_parallel::accumulate |
parallel/numeric |
std::adjacent_difference |
numeric |
__gnu_parallel::adjacent_difference |
parallel/numeric |
std::inner_product |
numeric |
__gnu_parallel::inner_product |
parallel/numeric |
std::partial_sum |
numeric |
__gnu_parallel::partial_sum |
parallel/numeric |
std::adjacent_find |
algorithm |
__gnu_parallel::adjacent_find |
parallel/algorithm |
std::count |
algorithm |
__gnu_parallel::count |
parallel/algorithm |
std::count_if |
algorithm |
__gnu_parallel::count_if |
parallel/algorithm |
std::equal |
algorithm |
__gnu_parallel::equal |
parallel/algorithm |
std::find |
algorithm |
__gnu_parallel::find |
parallel/algorithm |
std::find_if |
algorithm |
__gnu_parallel::find_if |
parallel/algorithm |
std::find_first_of |
algorithm |
__gnu_parallel::find_first_of |
parallel/algorithm |
std::for_each |
algorithm |
__gnu_parallel::for_each |
parallel/algorithm |
std::generate |
algorithm |
__gnu_parallel::generate |
parallel/algorithm |
std::generate_n |
algorithm |
__gnu_parallel::generate_n |
parallel/algorithm |
std::lexicographical_compare |
algorithm |
__gnu_parallel::lexicographical_compare |
parallel/algorithm |
std::mismatch |
algorithm |
__gnu_parallel::mismatch |
parallel/algorithm |
std::search |
algorithm |
__gnu_parallel::search |
parallel/algorithm |
std::search_n |
algorithm |
__gnu_parallel::search_n |
parallel/algorithm |
std::transform |
algorithm |
__gnu_parallel::transform |
parallel/algorithm |
std::replace |
algorithm |
__gnu_parallel::replace |
parallel/algorithm |
std::replace_if |
algorithm |
__gnu_parallel::replace_if |
parallel/algorithm |
std::max_element |
algorithm |
__gnu_parallel::max_element |
parallel/algorithm |
std::merge |
algorithm |
__gnu_parallel::merge |
parallel/algorithm |
std::min_element |
algorithm |
__gnu_parallel::min_element |
parallel/algorithm |
std::nth_element |
algorithm |
__gnu_parallel::nth_element |
parallel/algorithm |
std::partial_sort |
algorithm |
__gnu_parallel::partial_sort |
parallel/algorithm |
std::partition |
algorithm |
__gnu_parallel::partition |
parallel/algorithm |
std::random_shuffle |
algorithm |
__gnu_parallel::random_shuffle |
parallel/algorithm |
std::set_union |
algorithm |
__gnu_parallel::set_union |
parallel/algorithm |
std::set_intersection |
algorithm |
__gnu_parallel::set_intersection |
parallel/algorithm |
std::set_symmetric_difference |
algorithm |
__gnu_parallel::set_symmetric_difference |
parallel/algorithm |
std::set_difference |
algorithm |
__gnu_parallel::set_difference |
parallel/algorithm |
std::sort |
algorithm |
__gnu_parallel::sort |
parallel/algorithm |
std::stable_sort |
algorithm |
__gnu_parallel::stable_sort |
parallel/algorithm |
std::unique_copy |
algorithm |
__gnu_parallel::unique_copy |
parallel/algorithm |
对引言中不能顺利通过编译的示例进行改造,以便能借助GCC编译器实现STL并行算法,下面列出改进后的代码:
#include
#include
#include
#include
using namespace std;
using namespace __gnu_parallel;
bool odd(int n) { return n % 2; }
int main() {
// Set up the parallel mode.
_Settings s;
s.algorithm_strategy = force_parallel;
_Settings::set(s);
vector<int> d(500);
mt19937 gen;
uniform_int_distribution<int> dis(0, 100000);
auto rand_num([=]() mutable { return dis(gen); });
generate(begin(d), end(d), rand_num);
sort(begin(d), end(d));
reverse(begin(d), end(d));
auto odds(count_if(begin(d), end(d), odd));
cout << (100.0 * odds / d.size()) << "% of the numbers are odd.\n";
return 0;
}
编译指令为(注意-std=c++17
替换为-std=c++11
或-std=c++14均可
):
g++ -g -Wall -D_GLIBCXX_PARALLEL -fopenmp -std=c++17 gcc_parallel.cpp -o gcc_parallel
在我机器上的运行结果为:
53% of the numbers are odd.
odd
函数)、Lambda表达式(例如上例中的[=]() mutable { return dis(gen); }
)或函数对象内部绝不能抛出异常;