c++oding="utf-8" ?>
std::execution::par 在百万级 vector 排序中通常提速 2–4 倍,而 par_unseq 并非总更快,其性能依赖内存连续性、无分支及编译器 simd 展开能力,盲目使用可能因调度开销反而变慢。

直接上结论:std::execution::par 在百万级 std::vector<int></int> 排序中通常能带来 2–4 倍加速,但 std::execution::par_unseq 并不总更快——它依赖内存连续性、无分支操作和编译器对 SIMD 的实际展开能力,盲目替换反而可能因指令调度开销变慢。
std::sort 配合 execution 策略的实测表现差异
不同策略在相同数据规模(100 万 int)下表现差异显著,不是“越并行越快”:
-
std::execution::seq:单线程,稳定但无并发收益;适合调试或小数据( -
std::execution::par:启用线程池(libstdc++ 默认用std::thread::hardware_concurrency()),对std::sort这类分治算法天然友好;实测在 8 核 CPU 上平均提速约 3.2× -
std::execution::par_unseq:要求排序内部比较/交换操作可向量化——但std::sort的 pivot 选择和分区逻辑含分支与指针跳转,主流实现(GCC 13/Clang 18)几乎不对其做 SIMD 优化;此时它退化为par,还多一层调度判断开销
简短验证代码:
#include <execution> #include <vector> #include <algorithm> #include <chrono><p>auto t0 = std::chrono::high_resolution_clock::now(); std::sort(std::execution::par, v.begin(), v.end()); auto t1 = std::chrono::high_resolution_clock::now(); </p></chrono></algorithm></vector></execution>
哪些算法真正吃得住 par_unseq?
par_unseq 不是给所有算法“加个前缀就加速”,它只对满足以下条件的操作有效:
- 迭代器指向**连续内存**(
std::vector、std::array可,std::list不行) - 操作函数是**无状态、无副作用、无分支、无函数调用**的纯计算(如
[](int x) { return x * x + 2 * x + 1; }) - 标准库实现支持该算法的向量化路径(目前仅
std::transform、std::reduce、std::for_each在部分场景下有实质 SIMD 生成)
例如这个能榨出 SIMD 的例子:
std::vector<float> a(1'000'000), b(1'000'000), c(1'000'000);
std::transform(std::execution::par_unseq,
a.begin(), a.end(), b.begin(), c.begin(),
[](float x, float y) { return x * x + y * y; });
</float>
而 std::sort、std::stable_sort、std::nth_element 等含控制流的算法,par_unseq 和 par 行为基本一致。
编译器与标准库支持现状(2026 年实测)
别只看标准文档——当前(GCC 13.3 / Clang 18.1 / MSVC 19.41)对 std::execution 的支持仍存在明显断层:
- libstdc++(GCC):完整支持
seq/par,par_unseq对std::transform会尝试 AVX2 展开,但对std::sort完全忽略 - libc++(Clang):
par使用自建线程池,par_unseq在 macOS 上默认禁用(需-D_LIBCPP_ENABLE_CXX20_EXECUTION_POLICY) - MSVC:
par基于 Windows 线程池,par_unseq仅对std::for_each启用 SSE2,且不保证跨版本 ABI 兼容
一个容易踩的坑:std::execution::par 调用失败时不会抛异常,而是静默回退到 seq ——你得用 std::this_thread::get_id() 或 perf 工具确认线程是否真被调度。
排序性能测试必须控制的变量
想靠一次 std::sort 测试得出“哪个策略最快”,大概率被误导。以下变量必须固定:
- 数据初始分布:完全随机 vs 已近似有序 vs 逆序 ——
par在随机数据上优势最明显,逆序时线程负载不均会导致加速比骤降 - 内存分配方式:用
std::vector::reserve()预分配,避免push_back触发多次 realloc 导致 cache miss - 编译选项:必须开启
-O3 -march=native,否则par_unseq的向量化根本不会触发 - 系统干扰:关闭 CPU 频率调节(
sudo cpupower frequency-set -g performance),避免 turbo boost 波动影响计时
真正可靠的测试不是跑一次,而是用 google-benchmark 跑 100 轮取中位数,并检查每轮的线程数是否稳定(htop 或 perf stat -e task-clock,context-switches)。
最常被忽略的一点:执行策略只控制“怎么跑”,不改变算法复杂度。即使用了 par,std::sort 还是 O(n log n),只是常数项下降;如果数据量只有几千,线程创建/同步开销反而超过收益——这时候 seq 才是最优解。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











