并行模式库 (PPL) 提供了对数据集合并行地执行工作的算法。这些算法类似于 C++ 标准库提供的算法。并行算法由并发运行时中的现有功能组成。
PPL 提供三种排序算法:concurrency::parallel_sort、concurrency::parallel_buffered_sort 和 concurrency::parallel_radixsort。 当您具有可受益于并行排序的数据集时,这些排序算法很有用。 具体而言,当您具有大型数据集时或使用需要消耗大量计算资源的比较操作对数据进行排序时,并行排序很有用。 每种算法都会就地对元素排序。
parallel_sort 和 parallel_buffered_sort 算法都是基于比较的算法。 即,它们按值来比较元素。 parallel_sort 算法没有其他内存要求,适用于通用排序。 parallel_buffered_sort 算法的性能好于 parallel_sort,但是它需要 O(N) 空间。
parallel_radixsort 算法是基于哈希的。 即,它使用整数键来对元素排序。 通过使用键,此算法可以直接计算元素的目标,而不是使用比较。 与 parallel_buffered_sort 类似,此算法需要 O(N) 空间。
下表总结了三种并行排序算法的重要属性。
下图用图形更形象地说明了三种并行排序算法的重要属性。
这些并行排序算法支持移动语义。 可以定义一个移动赋值运算符,以使交换操作的出现更有效。 有关移动语义和移动赋值运算符的详细信息,请参阅 Rvalue 引用声明符:&& 和移动构造函数和移动赋值运算符 (C++)。 如果您不提供移动赋值运算符或交换函数,排序算法将使用复制构造函数。
下面的基本示例演示如何使用 parallel_sort 对 vector 值的 int 进行排序。 默认情况下,parallel_sort 使用 std::less 来比较值。
// basic-parallel-sort.cpp
// compile with: /EHsc
#include <ppl.h>
#include <random>
#include <iostream>
using namespace concurrency;
using namespace std;
int wmain()
{
// Create and sort a large vector of random values.
vector<int> values(25000000);
generate(begin(values), end(values), mt19937(42));
parallel_sort(begin(values), end(values));
// Print a few values.
wcout << "V(0) = " << values[0] << endl;
wcout << "V(12500000) = " << values[12500000] << endl;
wcout << "V(24999999) = " << values[24999999] << endl;
}
/* Output:
V(0) = -2147483129
V(12500000) = -427327
V(24999999) = 2147483311
*/
此示例演示如何为 parallel_radixsort 算法提供哈希函数。 此示例对三维点排序。 根据这些点与参考点的距离对它们进行排序。
// parallel-sort-points.cpp
// compile with: /EHsc
#include <ppl.h>
#include <random>
#include <iostream>
using namespace concurrency;
using namespace std;
// Defines a 3-D point.
struct Point
{
int X;
int Y;
int Z;
};
// Computes the Euclidean distance between two points.
size_t euclidean_distance(const Point& p1, const Point& p2)
{
int dx = p1.X - p2.X;
int dy = p1.Y - p2.Y;
int dz = p1.Z - p2.Z;
return static_cast<size_t>(sqrt((dx*dx) + (dy*dy) + (dz*dz)));
}
int wmain()
{
// The central point of reference.
const Point center = { 3, 2, 7 };
// Create a few random Point values.
vector<Point> values(7);
mt19937 random(42);
generate(begin(values), end(values), [&random] {
Point p = { random()%10, random()%10, random()%10 };
return p;
});
// Print the values before sorting them.
wcout << "Before sorting:" << endl;
for_each(begin(values), end(values), [center](const Point& p) {
wcout << L'(' << p.X << L"," << p.Y << L"," << p.Z
<< L") D = " << euclidean_distance(p, center) << endl;
});
wcout << endl;
// Sort the values based on their distances from the reference point.
parallel_radixsort(begin(values), end(values),
[center](const Point& p) -> size_t {
return euclidean_distance(p, center);
});
// Print the values after sorting them.
wcout << "After sorting:" << endl;
for_each(begin(values), end(values), [center](const Point& p) {
wcout << L'(' << p.X << L"," << p.Y << L"," << p.Z
<< L") D = " << euclidean_distance(p, center) << endl;
});
wcout << endl;
}
/* Output:
Before sorting:
(2,7,6) D = 5
(4,6,5) D = 4
(0,4,0) D = 7
(3,8,4) D = 6
(0,4,1) D = 7
(2,5,5) D = 3
(7,6,9) D = 6
After sorting:
(2,5,5) D = 3
(4,6,5) D = 4
(2,7,6) D = 5
(3,8,4) D = 6
(7,6,9) D = 6
(0,4,0) D = 7
(0,4,1) D = 7
*/
为了便于演示,本示例使用相对较小的数据集。 您可以增大向量的初始范围,体验较大数据集中性能的提升。
此示例使用 lambda 表达式作为哈希函数。 还可以使用 std::hash 类的一个内置实现,或者定义自己的专用化。 如本示例所示,您还可以使用自定义哈希函数对象:
// Functor class for computing the distance between points.
class hash_distance
{
public:
hash_distance(const Point& reference)
: m_reference(reference)
{
}
size_t operator()(const Point& pt) const {
return euclidean_distance(pt, m_reference);
}
private:
Point m_reference;
};
// Use hash_distance to compute the distance between points.
parallel_radixsort(begin(values), end(values), hash_distance(center));
哈希函数必须返回整型类型(std::is_integral::value 必须为 true)。 此整型必须可转换为类型 size_t。