文章目录
前言
利用多核cpu加速算法在目前看来已经不是什么新鲜事了。在C++17中,推出了算法函数的执行策略,可以选择执行策略来优化算法的执行效果。(注意不是所有算法都支持)
目前到C++20,已经支持了四种执行策略。当然本文也仅仅是做简单的展示和描述,因此内部细节十分复杂不是几篇端文章就能看懂的。
标准 | 策略 | 执行策略类型(类) | 全局执行策略对象(常量) |
---|---|---|---|
C++17 | 串行执行 | sequenced_policy | std::execution::seq |
C++17 | 并行执行 | parallel_policy | std::execution::par |
C++17 | 并行无序执行 | parallel_unsequenced_policy | std::execution::par_unseq |
C++20 | 无序执行 | unsequenced_policy | std::execution::unseq |
功能特性测试 | 值 | 标准 | 功能特性 |
---|---|---|---|
__cpp_lib_parallel_algorithm | 201603L | C++17 | 并行算法 |
__cpp_lib_execution | 201603L | C++17 | 执行策略 |
__cpp_lib_execution | 201902L | C++20 | std::execution::unsequenced_policy |
Code测试
Code
这里仅展示一个比较简单的小例子,仅做小参考。
#include <algorithm>
#include <ctime>
#include <execution>
#include <iostream>
#include <string>
#include <vector>
/**
* 辅助计时类
*/
class Timer {
std::string str;
clock_t start;
public:
Timer(const std::string& str) : str(str) {
start = clock();
}
~Timer() {
clock_t end = clock();
std::cout << str << " => " << (end - start) / 1000.0 << '\n';
}
};
/**
* 串行执行策略
* class sequenced_policy;
*/
void test_sequenced_policy(std::vector<int> arr) {
Timer timer("std::execution::seq");
std::sort(std::execution::seq, arr.begin(), arr.end());
}
/**
* 并行执行策略
* class parallel_policy;
*/
void test_parallel_policy(std::vector<int> arr) {
Timer timer("std::execution::par");
std::sort(std::execution::par, arr.begin(), arr.end());
}
/**
* 并行无序执行策略
* class parallel_unsequenced_policy;
*/
void test_parallel_unsequenced_policy(std::vector<int> arr) {
Timer timer("std::execution::par_unseq");
std::sort(std::execution::par_unseq, arr.begin(), arr.end());
}
/**
* 无序执行策略
* class unsequenced_policy;
*/
void test_unsequenced_policy(std::vector<int> arr) {
#if __cpp_lib_execution >= 201902L
Timer timer("std::execution::unseq");
std::sort(std::execution::unseq, arr.begin(), arr.end());
#endif
}
int main() {
// help data
std::vector<int> arr;
for (int i = 0; i < 3.0 * 100'000'00; i += 1) {
arr.push_back(rand());
}
test_sequenced_policy(arr);
test_parallel_policy(arr);
test_parallel_unsequenced_policy(arr);
test_unsequenced_policy(arr);
}
运行效果
使用编译器:vs2019 => _MSC_VER = 1929
这只是基于笔者本地的单词测试,仅作参考。
x86-Debug
std::execution::seq => 22.874 std::execution::par => 5.495 std::execution::par_unseq => 5.854 std::execution::unseq => 22.864
x86-Release
std::execution::seq => 1.249 std::execution::par => 0.258 std::execution::par_unseq => 0.253 std::execution::unseq => 1.201
x64-Debug
std::execution::seq => 8.705 std::execution::par => 2.371 std::execution::par_unseq => 2.402 std::execution::unseq => 8.737
x64-Release
std::execution::seq => 1.179 std::execution::par => 0.25 std::execution::par_unseq => 0.238 std::execution::unseq => 1.129
msvc源码描述
在以任何这些执行策略执行并行算法的过程中,若元素访问函数的调用因未捕获的异常退出,则调用 std::terminate,但实现可以定义以其他方式处理异常的额外执行策略。
源码
笔者看过
gcc 11.2.0
的实现,是用函数直接返回true-or-false的方式来区分。但是注释基本没没什么描述,因此就不展示了。
本代码于msvc中<execution>
。
可见这里其实是定义了四个类型标签。标准没有定义对象内部的属性,这里是msvc的实现问题。
这里主要可以直接根据msvc的人话注释来理解标签的有含义。
namespace execution {
class sequenced_policy {
// indicates support for only sequential execution, and requests termination on exceptions
public:
using _Standard_execution_policy = int;
static constexpr bool _Parallelize = false;
static constexpr bool _Ivdep = false;
};
inline constexpr sequenced_policy seq{/* unspecified */};
class parallel_policy {
// indicates support by element access functions for parallel execution with parallel forward progress
// guarantees, and requests termination on exceptions
public:
using _Standard_execution_policy = int;
static constexpr bool _Parallelize = true;
static constexpr bool _Ivdep = true;
};
inline constexpr parallel_policy par{/* unspecified */};
class parallel_unsequenced_policy {
// indicates support by element access functions for parallel execution with weakly parallel forward progress
// guarantees, and requests termination on exceptions
//
// (at this time, equivalent to parallel_policy)
public:
using _Standard_execution_policy = int;
static constexpr bool _Parallelize = true;
static constexpr bool _Ivdep = true;
};
inline constexpr parallel_unsequenced_policy par_unseq{/* unspecified */};
#if _HAS_CXX20
class unsequenced_policy {
// indicates support by element access functions for weakly parallel forward progress guarantees, and for
// executing interleaved on the same thread, and requests termination on exceptions
//
// (at this time, equivalent to sequenced_policy except for the for_each family)
public:
using _Standard_execution_policy = int;
static constexpr bool _Parallelize = false;
static constexpr bool _Ivdep = true;
};
inline constexpr unsequenced_policy unseq{/* unspecified */};
#endif // _HAS_CXX20
} // namespace execution
std::sequenced_policy seq
- 执行策略类型,用作对并行算法重载消歧义的独有类型,并要求并行算法的执行可以不并行化。以此策略调用(通常以 std::execution::seq 指定)的并行算法中,元素访问函数的调用在调用方线程中是非确定顺序的。
/**
* 串行策略
* indicates support for only sequential execution
* 指示仅支持顺序执行
* and requests termination on exceptions
* 并在异常时终止请求
*/
class sequenced_policy {
// indicates support for only sequential execution, and requests termination on exceptions
public:
using _Standard_execution_policy = int;
static constexpr bool _Parallelize = false;
static constexpr bool _Ivdep = false;
};
inline constexpr sequenced_policy seq{/* unspecified */};
std::parallel_policy par
- 执行策略类型,用作对并行算法重载消歧义的独有类型,并指示并行算法的执行可以并行化。以此策略调用(通常以 std::execution::par 指定)的并行算法中,元素访问函数的调用允许在调用方线程,或由库隐式创建的用以支持并行算法执行的线程中执行。任何执行于同一线程中的这种调用彼此间是非确定顺序的,
/**
* 并行执行策略
* indicates support by element access functions for parallel execution with parallel forward
* progress guarantees
* 指示元素访问函数支持并行执行,并保证并行向前进度
* and requests termination on exceptions
* 并在异常时终止请求
*/
class parallel_policy {
// indicates support by element access functions for parallel execution with parallel forward
// progress guarantees, and requests termination on exceptions
public:
using _Standard_execution_policy = int;
static constexpr bool _Parallelize = true;
static constexpr bool _Ivdep = true;
};
inline constexpr parallel_policy par{/* unspecified */};
std::parallel_unsequenced_policy par_unseq
- 执行策略类型,用作对并行算法重载消歧义的独有类型,并指示并行算法的执行可以并行化、向量化,或在线程间迁移(例如用亲代窃取式调度器)。以此策略调用的并行算法中,容许在未指定线程中以无序方式来执行元素访问函数调用,并相对于各个线程中的另一调用间无顺序。
/**
* 并行无序执行策略
* indicates support by element access functions for parallel execution with weakly parallel
* forward progress guarantees
* 指示元素访问函数支持并行执行,并提供弱并行向前进度保证
* and requests termination on exceptions
* 并在异常时终止请求
* (at this time, equivalent to parallel_policy)
* (此时,相当于并行策略)
*/
class parallel_unsequenced_policy {
// indicates support by element access functions for parallel execution with weakly parallel
// forward progress guarantees, and requests termination on exceptions
//
// (at this time, equivalent to parallel_policy)
public:
using _Standard_execution_policy = int;
static constexpr bool _Parallelize = true;
static constexpr bool _Ivdep = true;
};
inline constexpr parallel_unsequenced_policy par_unseq{/* unspecified */};
std::unsequenced_policy unseq
- 执行策略类型,用作对并行算法重载消歧义的独有类型,并指示可将算法的执行向量化,例如在单个线程上使用操作多个数据项的指令执行。
#if _HAS_CXX20
/**
* 无序执行策略
* indicates support by element access functions for weakly parallel forward progress guarantees
* 指示元素访问函数支持弱并行向前进度保证
* and for executing interleaved on the same thread
* 支持在同一线程上交错执行
* and requests termination on exceptions
* 以及在异常时终止请求
* (at this time, equivalent to sequenced_policy except for the for_each family)
* (此时,相当于sequenced_policy,除了for_each一类)
*/
class unsequenced_policy {
// indicates support by element access functions for weakly parallel forward progress
// guarantees, and for executing interleaved on the same thread, and requests termination on
// exceptions
//
// (at this time, equivalent to sequenced_policy except for the for_each family)
public:
using _Standard_execution_policy = int;
static constexpr bool _Parallelize = false;
static constexpr bool _Ivdep = true;
};
inline constexpr unsequenced_policy unseq{/* unspecified */};
#endif // _HAS_CXX20
END
关注我,学习更多C/C++,算法,计算机知识