并行模式库 (PPL) 提供了对数据集合并行地执行工作的算法。 这些算法类似于 C++ 标准库提供的算法。
并行算法由并发运行时中的现有功能组成。 例如,concurrency::parallel_for 算法使用 concurrency::structured_task_group 对象执行并行循环迭代。 根据可用的计算资源数,parallel_for 算法以最佳的方式对工作进行分区。
parallel_for 算法
concurrency::parallel_for 算法重复地以并行方式执行相同的任务。 其中的每个任务都由迭代值进行参数化。 如果循环体不在该循环的迭代之间共享资源,则此算法非常有用。
parallel_for 算法以最佳方式对任务进行分区,以实现并行执行。 当工作负载不平衡时,此算法还会使用工作窃取算法和范围窃取来平衡这些分区。 当一个循环迭代协同阻塞时,运行时会将分配给当前线程的迭代范围重新分配给其他线程或处理器。 同样地,当一个线程完成一个迭代范围时,运行时会将其他线程的工作重新分配给该线程。 parallel_for 算法还支持嵌套并行。 当一个并行循环包含另一个并行循环时,运行时会以一种有效的方式协调处理循环主体之间的资源,以实现并行执行。
parallel_for 算法有多个重载版本。 第一个版本采用一个起始值、一个结束值和一个工作函数(Lambda 表达式、函数对象或函数指针)。 第二个版本采用一个起始值、一个结束值、一个步长值和一个工作函数。 此函数的第一个版本使用 1 作为步长值。 其余版本采用分区程序对象,使您能够指定 parallel_for 如何在线程之间对范围进行分区。 本文档的分区工作一节中更详细地介绍了分区程序。
可以转换多个 for 循环以使用 parallel_for。 但是,parallel_for 算法在以下方面与 for 语句不同:
parallel_for 算法 parallel_for 不会按预先确定的顺序执行任务。
parallel_for 算法不支持任意终止条件。 当迭代变量的当前值小于 last 时,算法 parallel_for 将停止。
_Index_type 类型参数必须是整型类型。 此整型类型可以有符号,也可以无符号。
循环迭代必须是前向的。 如果 _Step 参数小于 1,算法 parallel_for 将引发 std::invalid_argument 类型的异常。
parallel_for 算法的异常处理机制不同于 for 循环的异常处理机制。 如果在并行循环体中同时发生多个异常,运行时只会将其中一个异常传播到 parallel_for 线程。 此外,当一个循环迭代引发异常时,运行时不会立即停止整个循环。 相反,循环被置于取消状态,运行时会放弃尚未启动的任何任务。 有关异常处理和并行算法的详细信息,请参阅异常处理。
尽管 parallel_for 算法不支持任意终止条件,但你可以使用取消来停止所有任务。
负载均衡和支持取消等功能所带来的计划成本对于并行执行循环体的好处来说可能不是经济高效的,尤其是在循环体相对较小时。 您可以在并行循环中使用分区程序来尽量减少此开销。
示例
以下示例展示了 parallel_for 算法的基本结构。 本示例并行输出到控制台中范围 [1, 5] 内的每个值。
// parallel-for-structure.cpp
// compile with: /EHsc
#include <ppl.h>
#include <array>
#include <sstream>
#include <iostream>
using namespace concurrency;
using namespace std;
int wmain()
{
// Print each value from 1 to 5 in parallel.
parallel_for(1, 6, [](int value) {
wstringstream ss;
ss << value << L' ';
wcout << ss.str();
});
}
输出:
1 2 4 3 5
由于 parallel_for 算法对每个项并行执行操作,因此将值输出到控制台的顺序会有所不同。
parallel_for_each 算法
concurrency::parallel_for_each 算法以并行方式对迭代容器执行任务,例如由 C++ 标准库提供的任务。 它与 parallel_for 算法使用相同的分区逻辑。
parallel_for_each 算法类似于 C++ 标准库 std::for_each 算法,但是 parallel_for_each 算法会并行执行任务。 与其他并行算法一样,parallel_for_each 不会按特定顺序执行任务。
尽管 parallel_for_each 算法同时适用于向前迭代器和随机访问迭代器,但它在使用随机访问迭代器时性能更好。
示例
以下示例展示了 parallel_for_each 算法的基本结构。 本示例将 std::array 对象中的每个值并行输出到控制台中。
// parallel-for-each-structure.cpp
// compile with: /EHsc
#include <ppl.h>
#include <array>
#include <sstream>
#include <iostream>
using namespace concurrency;
using namespace std;
int wmain()
{
// Create an array of integer values.
array<int, 5> values = { 1, 2, 3, 4, 5 };
// Print each value in the array in parallel.
parallel_for_each(begin(values), end(values), [](int value) {
wstringstream ss;
ss << value << L' ';
wcout << ss.str();
});
}
/* Sample output:
5 4 3 1 2
*/
由于 parallel_for_each 算法对每个项并行执行操作,因此将值输出到控制台的顺序会有所不同。