c - C中快速排序的并行化
问题描述
对于一个作业,我得到了一个用 C 语言编写的快速排序程序的完整功能顺序版本,但现在我需要使用线程通过并行化来加速它。下面修改后的程序正在排序并且正在使用线程(使用 >100% CPU),但是现在按顺序执行时比以前需要更长的时间。
该程序的工作方式是,当第 8 个线程终止时,程序现在应该继续按顺序执行程序的其余部分。
我知道这可能不是使用线程进行优化时的可选方式,但这就是该程序应该如何工作的方式。我想这与 . 有一些关系pthread_join
,但我试图移动它,但这没有任何区别,有时甚至更糟。
*注意,我确实在这里包含了数组的初始化,因为它不相关。
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#define THREADS 8
#define KILO (1024)
#define MEGA (1024*1024) //1 048 576
#define MAX_ITEMS (64 *MEGA) //67 108 864
#define swap(v, a, b) {unsigned tmp; tmp=v[a]; v[a]=v[b]; v[b]=tmp;}
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
static int *v;
int global_thread = 1; //Starts at 1, because the thread from main is the first
struct threadArgs
{
int low;
int high;
};
static int partition(int *v, int low, int high, int pivot_index)
{
if (pivot_index != low)
swap(v, low, pivot_index);
pivot_index = low;
low++;
while (low <= high)
{
if (v[low] <= v[pivot_index])
low++;
else if (v[high] > v[pivot_index])
high--;
else
swap(v, low, high);
}
if (high != pivot_index)
swap(v, pivot_index, high);
return high;
}
void* quick_sort(void* arguments)
{
struct threadArgs* parent = (struct threadArgs*) arguments;
pthread_t child;
struct threadArgs* child_thread;
child_thread = malloc(sizeof(struct threadArgs));
int pivot_index;
int low = parent->low;
int high = parent->high;
if (low >= high)
return NULL;
pivot_index = (low + high) / 2;
pivot_index = partition(v, low, high, pivot_index);
if(global_thread < THREADS) //should only create 7 new threads, 1 is already from main
{
if(pivot_index < high) //child: right side
{
child_thread->low = pivot_index +1;
child_thread->high = high;
pthread_mutex_lock(&lock);
global_thread++;
pthread_mutex_unlock(&lock);
pthread_create(&child, NULL, quick_sort, (void*) child_thread);
}
if(low < pivot_index) //parent: left side,
{
parent->low = low;
parent->high = pivot_index - 1;
quick_sort((void*) parent);
}
pthread_join(child, NULL);
}
else
{
if (low < pivot_index) //left side sequential
{
parent->low = low;
parent->high = pivot_index - 1;
quick_sort((void*) parent);
}
if (pivot_index < high) //right side sequential
{
parent->low = pivot_index +1;
parent->high = high;
quick_sort((void*) parent);
}
}
return NULL;
}
int main(int argc, char **argv)
{
init_array();
struct threadArgs* args; // main thread
args = malloc(sizeof(struct threadArgs));
args->low = 0;
args->high = MAX_ITEMS - 1;
quick_sort((void *)args);
pthread_mutex_destroy(&lock);
free(args);
return 0;
}
解决方案
您的代码存在严重的内存泄漏:每次调用都会quick_sort()
为子线程的参数分配内存,即使它最终没有启动新的子线程,并且没有任何内存被释放。 malloc()
相对昂贵,并且根据您的实现,它可能会获得锁,从而产生瓶颈。
您的代码存在数据竞争:在互斥锁或其他同步对象的保护之外quick_sort()
读取共享变量global_threads
,并且该变量也被您的某些线程修改。因此,产生的行为是未定义的。
您的代码有一个缺陷,即global_thread < THREADS
您的函数分支quick_sort()
仅有条件地启动一个新线程,但无条件地尝试加入一个新线程。如果没有启动子线程,则连接尝试将由于读取child
不确定的变量值而产生未定义的行为。然而,在实践中使用随机数据不太可能发生这种情况。
建议:
移动
malloc()
调用,以便仅在实际要创建新线程的情况下进行分配。由于每次分配的数量很小并且分配的数量也会很小,您可能可以继续允许该内存泄漏,但最好在加入孩子之后释放它。更改决定是否启动子线程的策略。例如,跟踪递归深度,并仅在较浅的深度(两个或三个级别)启动一个新子级。然后,您不需要共享变量或互斥锁,只需
struct threadArgs
.确保不要在尚未启动子线程时尝试加入子线程。
实现留作练习,但我自己对这些修复的实现产生了显着的单线程性能改进,并且通过添加更多线程表现出合理(但不是很好)的加速:两个线程的加速大约 30%,略高于 50%有四个。
推荐阅读
- flutter - 颤振:主题!ThemeData.light 在第一时间运行良好,但是当您从 ThemeData.dark 更改它时,它(浅色主题)不起作用
- javascript - 在 React 的弹出窗口中检查 URL 更改
- javascript - 即使使用 webpack 也没有定义需求
- python - 使用重新编码与 FFMPEG 自动连接
- php - phpSPO 库 - 未经授权的访问异常。需要图书馆基础知识的指导
- ios - 如何映射一个 Observable
到另一种类型的 Observable 在 RxSwift 中? - sql - 如何返回最近的值?
- c# - 强名称签名对此程序集无效
- flutter - 启动颤振应用程序时出错
- git - 如果其他分支已合并到主分支,如何将我的分支合并到主分支