C++17标准STL库并行策略在GCC编译器中的替代实现方法

本文介绍如何在GCC编译器中使用C++17标准的STL并行算法,包括必要的编译选项和宏定义,并提供了一个示例程序。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

C++17标准STL库并行策略在GCC编译器中的替代实现方法

严正声明:本文系作者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后续版本能尽快完成该特性的标准化。
1
下面是采用C++ 17标准执行策略方式实现并行计算的一个简单示例。该示例虽然符合C++ 17标准,但在我写作本文时,使用最新版本的GCC 7.3不能编译通过。

#include <algorithm>
#include <execution>
#include <iostream>
#include <random>
#include <vector>

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编译器使用并行计算所需的编译选项

使用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并行算法:

AlgorithmHeaderParallel algorithmParallel header
std::accumulatenumeric__gnu_parallel::accumulateparallel/numeric
std::adjacent_differencenumeric__gnu_parallel::adjacent_differenceparallel/numeric
std::inner_productnumeric__gnu_parallel::inner_productparallel/numeric
std::partial_sumnumeric__gnu_parallel::partial_sumparallel/numeric
std::adjacent_findalgorithm__gnu_parallel::adjacent_findparallel/algorithm
std::countalgorithm__gnu_parallel::countparallel/algorithm
std::count_ifalgorithm__gnu_parallel::count_ifparallel/algorithm
std::equalalgorithm__gnu_parallel::equalparallel/algorithm
std::findalgorithm__gnu_parallel::findparallel/algorithm
std::find_ifalgorithm__gnu_parallel::find_ifparallel/algorithm
std::find_first_ofalgorithm__gnu_parallel::find_first_ofparallel/algorithm
std::for_eachalgorithm__gnu_parallel::for_eachparallel/algorithm
std::generatealgorithm__gnu_parallel::generateparallel/algorithm
std::generate_nalgorithm__gnu_parallel::generate_nparallel/algorithm
std::lexicographical_comparealgorithm__gnu_parallel::lexicographical_compareparallel/algorithm
std::mismatchalgorithm__gnu_parallel::mismatchparallel/algorithm
std::searchalgorithm__gnu_parallel::searchparallel/algorithm
std::search_nalgorithm__gnu_parallel::search_nparallel/algorithm
std::transformalgorithm__gnu_parallel::transformparallel/algorithm
std::replacealgorithm__gnu_parallel::replaceparallel/algorithm
std::replace_ifalgorithm__gnu_parallel::replace_ifparallel/algorithm
std::max_elementalgorithm__gnu_parallel::max_elementparallel/algorithm
std::mergealgorithm__gnu_parallel::mergeparallel/algorithm
std::min_elementalgorithm__gnu_parallel::min_elementparallel/algorithm
std::nth_elementalgorithm__gnu_parallel::nth_elementparallel/algorithm
std::partial_sortalgorithm__gnu_parallel::partial_sortparallel/algorithm
std::partitionalgorithm__gnu_parallel::partitionparallel/algorithm
std::random_shufflealgorithm__gnu_parallel::random_shuffleparallel/algorithm
std::set_unionalgorithm__gnu_parallel::set_unionparallel/algorithm
std::set_intersectionalgorithm__gnu_parallel::set_intersectionparallel/algorithm
std::set_symmetric_differencealgorithm__gnu_parallel::set_symmetric_differenceparallel/algorithm
std::set_differencealgorithm__gnu_parallel::set_differenceparallel/algorithm
std::sortalgorithm__gnu_parallel::sortparallel/algorithm
std::stable_sortalgorithm__gnu_parallel::stable_sortparallel/algorithm
std::unique_copyalgorithm__gnu_parallel::unique_copyparallel/algorithm

三、GCC编译器使用STL并行算法的简单示例

对引言中不能顺利通过编译的示例进行改造,以便能借助GCC编译器实现STL并行算法,下面列出改进后的代码:

#include <iostream>
#include <parallel/algorithm>
#include <random>
#include <vector>

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.

四、说明

  1. GCC编译器中的STL并行算法是非异常安全的,也就是说,用户自定义函数(例如上例中的odd函数)、Lambda表达式(例如上例中的[=]() mutable { return dis(gen); })或函数对象内部绝不能抛出异常;
  2. 算法执行的顺序不确定,因为用户自定义函数、Lambda表达式或函数对象内部不能依赖于算法的执行顺序;
  3. 因为GCC OPenMP不支持在多线程中运行,因此不要尝试在多线程中调用STL并行算法。
<think>嗯,用户这次聚焦在C++17标准的具体编译器支持情况,看来ta正在做技术选型或项目迁移。从引用内容看,用户似乎在使用VisualStudio+MinGW的混合开发环境,可能涉及到跨平台编译需求。用户要求比较四大主流编译器(GCC/Clang/MSVC/ICC)对C++17的支持程度,这是个很落地的工程问题。需要特别注意三点:一是区分编译器的理论支持度和实际版本要求,二是明确核心特性支持差异,三是考虑实际开发中的启用方式。我注意到引用3里有个字符串分割函数实现,用户可能正在处理基础工具类开发,这说明ta需要编译器支持现代C++特性来优化代码。另外引用1提到protobuf版本冲突问题,暗示用户项目可能存在依赖管理需求。四大编译器支持情况需要分层说明:首先是理论支持里程碑,然后是关键特性实现状态。GCC和Clang的版本演进比较清晰,MSVC需要关注其从2017版开始的跳跃式改进,而ICC则需说明其与GCC版本的兼容关系。对于实际开发场景,编译选项的配置建议很重要,尤其用户提到CMake环境(cppreference[2]提到VS2022的CMake集成)。每个编译器都要给出启用C++17的最小标志参数,这对用户的工程配置直接有用。最后的相关问题设计要延续用户的技术路径:向下问C++20支持情况符合版本升级逻辑,并行处理特性回应现代CPU需求,模板推导改进关联C++17核心特性,文件系统则是实际工程刚需,而嵌入式场景覆盖用户可能的特殊环境。</think> 以下是各大主流编译器对 **C++17 标准**的支持情况总结(更新至 2023 年): --- ### **1. GCC (GNU Compiler Collection)** - **完全支持版本**:**GCC 8.1+** - **关键特性支持**: - 结构化绑定(`auto [a, b] = tuple;`) - `if constexpr`(编译期条件分支) - 类模板参数推导(`std::pair p{1, "a"};` 无需显式类型) - `std::optional`,`std::variant`,`std::any` - 并行 STL 算法(`std::execution::par`) - **启用标志**: ```bash g++ -std=c++17 main.cpp ``` --- ### **2. Clang** - **完全支持版本**:**Clang 5.0+** - **关键特性支持**: - C++17 核心语言特性全部支持 - 文件系统(`std::filesystem`)需链接 `libc++fs` 或 `libstdc++fs` - 折叠表达式(`(args + ...)`) - `constexpr lambda` - **启用标志**: ```bash clang++ -std=c++17 -stdlib=libc++ main.cpp # 推荐 libc++ ``` --- ### **3. MSVC (Microsoft Visual C++)** - **完全支持版本**:**Visual Studio 2017 v15.7+**(MSVC 19.14+) - **关键特性支持**: - C++17 语言特性已完整实现 - `<filesystem>` 需启用 `/std:c++17` 且链接 `std::filesystem` - 并行算法需额外安装 [Parallel STL](https://github.com/microsoft/STL)[^2] - **启用方式**: - **项目属性** → **C/C++** → **语言** → **C++语言标准** → **选择 C++17** - 或编译器标志:`/std:c++17` --- ### **4. Intel C++ Compiler (ICC)** - **完全支持版本**:**ICC 19.0+** - **关键特性支持**: - 对齐 GCC/Clang/MSVC 的 C++17 实现 - 支持 OpenMP 4.5+ 与并行 STL 优化 - 与 GCC 兼容,可使用 GCC 的 libstdc++ - **启用标志**: ```bash icpc -std=c++17 main.cpp ``` --- ### ⚙️ **跨平台项目建议** 1. **CMake 配置**: ```cmake cmake_minimum_required(VERSION 3.10) project(Example) set(CMAKE_CXX_STANDARD 17) # 强制使用 C++17 set(CMAKE_CXX_STANDARD_REQUIRED ON) ``` 2. **特性检测**: ```cpp #if __cplusplus >= 201703L // C++17 可用 #endif ``` --- ### 总结 | **编译器** | **最低完全支持版本** | **启用标志** | |------------------|----------------------|----------------------| | GCC | 8.1+ | `-std=c++17` | | Clang | 5.0+ | `-std=c++17` | | MSVC | VS2017 v15.7+ | `/std:c++17` | | Intel ICC | 19.0+ | `-std=c++17` | > ✅ 所有主流编译器已稳定支持 C++17,建议新项目直接启用。 --- ### 相关问题 1. **C++20 是否已可在生产环境中使用?各编译器的支持进展如何?** 2. **C++17并行算法(Parallel STL)在实际项目中的性能提升效果?** 3. **类模板参数推导(CTAD)如何简化泛型编程代码?** 4. **`std::filesystem` 在跨平台路径处理中有哪些最佳实践?** 5. **嵌入式开发中(如 ARM Cortex-M)使用 C++17 的可行性?**
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值