使用 std::thread
实现并行归并排序
#include <iostream>
#include <vector>
#include <thread>
#include <mutex>
std::mutex mtx;
// 合并两个有序数组
void merge(std::vector<int>& arr, int left, int mid, int right) {
int n1 = mid - left + 1;
int n2 = right - mid;
std::vector<int> L(n1), R(n2);
for (int i = 0; i < n1; i++)
L[i] = arr[left + i];
for (int j = 0; j < n2; j++)
R[j] = arr[mid + 1 + j];
int i = 0, j = 0, k = left;
while (i < n1 && j < n2) {
if (L[i] <= R[j]) {
arr[k] = L[i];
i++;
} else {
arr[k] = R[j];
j++;
}
k++;
}
while (i < n1) {
arr[k] = L[i];
i++;
k++;
}
while (j < n2) {
arr[k] = R[j];
j++;
k++;
}
}
// 并行归并排序
void parallelMergeSort(std::vector<int>& arr, int left, int right) {
if (left < right) {
int mid = left + (right - left) / 2;
std::thread leftThread(parallelMergeSort, std::ref(arr), left, mid);
std::thread rightThread(parallelMergeSort, std::ref(arr), mid + 1, right);
leftThread.join();
rightThread.join();
merge(arr, left, mid, right);
}
}
使用 OpenMP 实现并行归并排序
#include <iostream>
#include <vector>
#include <omp.h>
// 合并两个有序数组
void merge(std::vector<int>& arr, int left, int mid, int right) {
int n1 = mid - left + 1;
int n2 = right - mid;
std::vector<int> L(n1), R(n2);
for (int i = 0; i < n1; i++)
L[i] = arr[left + i];
for (int j = 0; j < n2; j++)
R[j] = arr[mid + 1 + j];
int i = 0, j = 0, k = left;
while (i < n1 && j < n2) {
if (L[i] <= R[j]) {
arr[k] = L[i];
i++;
} else {
arr[k] = R[j];
j++;
}
k++;
}
while (i < n1) {
arr[k] = L[i];
i++;
k++;
}
while (j < n2) {
arr[k] = R[j];
j++;
k++;
}
}
// 并行归并排序
void parallelMergeSortOpenMP(std::vector<int>& arr, int left, int right) {
if (left < right) {
int mid = left + (right - left) / 2;
#pragma omp parallel sections
{
#pragma omp section
{
parallelMergeSortOpenMP(arr, left, mid);
}
#pragma omp section
{
parallelMergeSortOpenMP(arr, mid + 1, right);
}
}
merge(arr, left, mid, right);
}
}
时间复杂度分析
- 顺序归并排序:时间复杂度为 (O(n \log n)),其中 (n) 是数组的长度。每次递归将数组分成两部分,共需要 (\log n) 层递归,每层递归的合并操作时间复杂度为 (O(n))。
- 并行归并排序:理想情况下,并行化后的时间复杂度仍然是 (O(n \log n)),但由于线程创建、同步等开销,实际运行时间可能会在接近 (O(n \log n)) 的范围内波动。并行化主要是利用多核处理器并行处理子问题,理论上可以将时间复杂度中的线性部分(合并操作)并行化,从而减少总运行时间。
性能瓶颈分析
- 线程创建和销毁开销:频繁创建和销毁线程(如
std::thread
实现中)会带来额外的开销,这可能会抵消并行化带来的性能提升。可以通过线程池技术来缓解这一问题。
- 线程同步开销:在合并操作时,虽然数据划分相对简单,但线程同步(如
std::mutex
或者 OpenMP 的隐式同步)会带来一定的开销。如果同步操作过于频繁或者粒度太粗,会影响并行效率。
- 负载均衡:如果任务划分不均匀,可能导致部分线程早早完成任务,而其他线程仍在忙碌,无法充分利用多核处理器的性能。在上述实现中,任务划分是均匀的,但在更复杂的场景下需要注意这一问题。