pthread实现快速排序

 

1. 实验内容与方法

  1. 初始化数组。数组中的值使用c++11中的random类随机生成0到2000之间的double值,将数组中的值存入到文件中,供后续调用使用以保证并行和串行进行快速排序时的数组是相同的。
  2. 程序计时。使用c++11中的std::chrono库,使用system_clock表示当前的系统时钟,系统中运行的所有进程使用now()得到的时间是一致的。
  3. 串行执行快速排序。不使用c++中的sort函数,手动实现快速排序。
  4. 并行执行快速排序。将数组分成n段,使用n个线程并行对每段进行快速排序处理。需要使用pthread_barrier函数保证所有线程都将分段排好序之后,进行类似归并排序。因为可能存在数组分段后长度不同的问题,自定义了一个类用来保存分段的左右端点位置。
  5. 数组大小和数组中的值不变,修改线程数。分别设置线程数为2、3、4、8个,统计运行时间。
  6. 线程数不变,修改数组大小。将数组大小分别修改为5000万和1亿,分别运行pthread统计运行时间。
  7.  

2. 实验过程

实验设备CPU(i7-7700  3.6GHz, 4核),内存16G,操作系统Ubuntu 18.04,IDE CLion。

2.1 运行时间

经过代码运行计时,得到以下的实验结果表格。

运行时间/秒

2000万

5000万

1亿

1核

6.72747

25.1391

76.2641

2核

3.36834

10.3938

27.2989

3核

2.39181

7.15399

17.0872

4核

2.01613

5.69332

13.0158

8核

2.09013

5.49922

11.2955

 

2.2 加速比

加速比

2000万

5000万

1亿

1核

1

1

1

2核

1.99726572

2.41866305

2.79366934

3核

2.81271088

3.51399708

4.46322979

4核

3.33682352

4.41554313

5.85934787

8核

3.21868496

4.57139376

6.75172414

 

2.3 图表

绘制成图表,如下图所示。

pthread实现快速排序_第1张图片

pthread实现快速排序_第2张图片

pthread实现快速排序_第3张图片

3  实验分析

从实验结果和加速比可以看出:

1 随着数据量的增大,快排所需的时间也相应增加;

2 在4核并行内,快排运行的加速比与运行的核心近似成正比;虽然系统显示为8核,但只有4个真正的核。所以当并行线程数大于4后,快排提升的效果并不明显,因为要进行线程的切换。

4 源代码

initVector.cpp

#include "iostream"
#include "fstream"
#include "random"
#include "vector"
#include "string"
#include "sstream"
#include "cstdlib"
using namespace std;
const int size = 20000000;

string path = "/home/dadan/Downloads/vector.txt";

void writeAndInitVector(int size) {
    vector res(size, 0.0);
    default_random_engine engine;
    uniform_real_distribution u(0.0, 20000);
    for (int i = 0; i < size; ++i) {
        res[i] = u(engine);
    }
    ofstream outfile(path);
    for (int i = 0; i < size; ++i) {
        outfile << res[i] << endl;
    }
    outfile.close();
}

void loadVector(int size) {
    vector res(size, 0);
    ifstream file(path);
    string line;
    for (int i = 0; i < size; ++i) {
        getline(file, line);
        res[i] = stod(line);
    }
    file.close();
}

int main() {
    writeAndInitVector(size);
    //loadVector(size);
    return 0;
}

quickSort.cpp

#include 
#include "pthread.h"
#include "vector"
#include "fstream"
#include "cstring"
#include "climits"
#include "chrono"
#include "ctime"

using namespace std;
using namespace std::chrono;

const long SIZE = 20000000; //the size of vector that need to be sorted
const int THREADS_NUM = 3; // the number of threads
long sortNumPerThread = SIZE / THREADS_NUM; // the number of vector that every thread needs to sort
string path = "/home/dadan/Downloads/vector.txt"; // file path of vector

vector vec(SIZE), sortedVec(SIZE);
pthread_barrier_t barrier;

class Section{
public:
    int left;
    int right;
    Section(){

    };
};

void loadVector();

void quickSort(int left, int right);

void *pthread_sort(void *arg);

void merge();

void serial_quick_sort();

int main() {
    //serial_quick_sort();
    loadVector();

    pthread_t tid;
    pthread_barrier_init(&barrier, NULL, THREADS_NUM + 1);
    auto start = system_clock::now();
    for (int i = 0; i < THREADS_NUM; ++i) {
        Section* section = new Section;
        section->left = i * sortNumPerThread;
        if (i == THREADS_NUM - 1) {
            section->right = SIZE - 1;
        } else {
            section->right = (i + 1) * sortNumPerThread - 1;
        }
        int status = pthread_create(&tid, NULL, pthread_sort, (void *) (section));
        if (status != 0) {
            cout << "create thread error" << endl;
            return -1;
        }
    }

    //main thread wait
    pthread_barrier_wait(&barrier);

    //merge results of per thread
    merge();
    auto end = system_clock::now();
    auto duration = duration_cast(end - start);
    cout << THREADS_NUM << " threads pthread-parallel quick sort takes "
         << double(duration.count()) * microseconds::period::num / microseconds::period::den << " seconds" << endl;
    return 0;
}

void loadVector() {
    ifstream read_file(path);
    string line;
    for (int i = 0; i < SIZE; ++i) {
        getline(read_file, line);
        vec[i] = stod(line);
    }
    read_file.close();
}

void quickSort(int start, int end) {
    if (start >= end)
        return;
    double base = vec[end];
    int left = start, right = end - 1;
    while (true) {
        while (vec[left] < base)
            ++left;
        while (vec[right] >= base)
            --right;
        if (left >= right)
            break;
        swap(vec[left], vec[right]);
    }
    if (vec[left] >= vec[end])
        swap(vec[left], vec[end]);
    else
        ++left;
    quickSort(start, left - 1);
    quickSort(left + 1, end);
}

void merge() {
    vector index(THREADS_NUM), index_most(THREADS_NUM);
    for (int i = 0; i < THREADS_NUM; ++i) {
        index[i] = i * sortNumPerThread;
        if (i == THREADS_NUM - 1) {
            index_most[i] = SIZE;
        } else {
            index_most[i] = (i + 1) * sortNumPerThread;
        }
    }
    for (int i = 0; i < SIZE; ++i) {
        int min_index;
        double min_num = INT_MAX;
        for (int j = 0; j < THREADS_NUM; ++j) {
            if ((index[j] < (j + 1) * sortNumPerThread) && (vec[index[j]] < min_num)) {
                min_num = vec[index[j]];
                min_index = j;
            }
        }
        sortedVec[i] = vec[index[min_index]];
        index[min_index]++;
    }
}

void serial_quick_sort() {
    loadVector();
    auto start = system_clock::now();
    quickSort(0, SIZE - 1);
    auto end = system_clock::now();
    auto duration = duration_cast(end - start);
    cout << "serial quick sort takes "
         << double(duration.count()) * microseconds::period::num / microseconds::period::den << " seconds" << endl;
}

void *pthread_sort(void *arg) {
    Section* section = (Section*)arg;
    quickSort(section->left, section->right);

    //wait other threads
    pthread_barrier_wait(&barrier);
}

 

你可能感兴趣的:(pthread实现快速排序)