[并行与分布式程序设计] OpenMP 并行编程----梯形积分法


#include <iostream>
#include <omp.h>
#include <pthread.h>
#include <cmath>
#include <sys/time.h>
using namespace std;

typedef struct {
    int thread_id;
    double a;
    double b;
    int n;
    int thread_num;
    double* global_val;
}thread_parm;

pthread_mutex_t mutex1;

double f(double);
// 返回值都为void* 便于test_time的范型
void* omp1_area(double, double, int, double*);
void* omp2_area(double, double, int, double*);
void* omp3_area(double, double, int, double*);
void* serial_area(double, double, int, double*);
void* pthread_area(void*);
long test_time(int, int, double*);

int main(int argc, const char * argv[]) {
    // n足够大时, 由于无法被thread_num整除带来的误差可以忽略不计
    int n = 100000000;
    for (int i = 0; i <= 5; i++){
        double global_val = 0;
        cout<<test_time(i, n, &global_val)<<"ms"<<endl;
        cout<<global_val<<endl<<endl;
    }
    return 0;
}

double f(double x) {
    return pow(x, 2);
}


/*
    在子线程中设置临界区
*/
void* omp1_area(double a, double b, int n, double* global_val) {
    int my_rank = omp_get_thread_num();
    int thread_count = omp_get_num_threads();
    
    double h;
    double local_a, local_b, my_result;
    int local_n;
    h = (b - a) / n;
    local_n = n/thread_count;
    local_a = a + my_rank * local_n * h;
    local_b = local_a + local_n * h;
    my_result = (f(local_a) + f(local_b)) / 2;
    // 先局部进行求和 避免过多通信
    // 忽略z线程组不能平均分配所有任务而导致最后几个点没有计算的误差
    for (int i = 1; i < local_n; i++) {
        double x = local_a + i * h;
        my_result += f(x);
    }
    my_result = my_result * h;
    // 用临界区保证全局求和正确性
    # pragma omp critical
    *global_val += my_result;
    return nullptr;
}

/*
    在主线程中设置临界区
*/
void* omp2_area(double a, double b, int n, double* global_val) {
    int my_rank = omp_get_thread_num();
    int thread_count = omp_get_num_threads();
    
    double h;
    double local_a, local_b;
    double* my_result = (double*)malloc(sizeof(double));
    int local_n;
    h = (b - a) / n;
    local_n = n/thread_count;
    local_a = a + my_rank * local_n * h;
    local_b = local_a + local_n * h;
    *my_result = (f(local_a) + f(local_b)) / 2;
    for (int i = 1; i < local_n; i++) {
        double x = local_a + i * h;
        *my_result += f(x);
    }
    *my_result = *my_result * h;
    return (void*)my_result;
}

/*
 使用并行for循环
 */
void* omp3_area(double a, double b, int n, double* global_val) {
    double h, approx;
    h = (b - a) / n;
    approx = (f(a) + f(b)) / 2;
#pragma omp parallel for num_threads(4) reduction(+:approx)
    for (int i = 1; i < n; i++) {
        approx += f(a + i * h);
    }
    *global_val = approx * h;
    return nullptr;
}

void* pthread_area(void* parm) {
    thread_parm* p = (thread_parm*)parm;
    int thread_id = p->thread_id;
    int thread_num = p->thread_num;
    double a = p->a;
    double b = p->b;
    int n = p->n;
    double* global_val = p->global_val;
    double h = (b - a) / n;
    int local_n = n / thread_num;
    double local_a = a + thread_id * h * local_n;
    double local_b = local_a + h * local_n;
    double my_result = (f(local_a) + f(local_b)) / 2;
    double x;
    for (int i = 1; i < local_n; i++) {
        x = local_a + h * i;
        my_result += f(x);
    }
    my_result *= h;
    pthread_mutex_lock(&mutex1);
    *global_val += my_result;
    pthread_mutex_unlock(&mutex1);
    
    pthread_exit(nullptr);
}

void* serial_area(double a, double b, int n, double* global_val) {
    double h, approx;
    h = (b - a) / n;
    approx = (f(a) + f(b)) / 2;
    for (int i = 1; i < n; i++) {
        approx += f(a + i * h);
    }
    *global_val = approx * h;
    return nullptr;
}

long test_time(int algorithm_type, int n, double* global_val) {
    struct timeval start, end;
    long total;
    if (algorithm_type == 0) {
        cout<<"Serial:"<<endl;
        gettimeofday(&start, NULL);
        serial_area(0, 1, n, global_val);
    }
    else if (algorithm_type == 1) {
        cout<<"OMP1:"<<endl;
        gettimeofday(&start, NULL);
        # pragma omp parallel num_threads(4)
        omp1_area(0, 1, n, global_val);
    }
    else if (algorithm_type == 2) {
        cout<<"OMP2:"<<endl;
        gettimeofday(&start, NULL);
        # pragma omp parallel num_threads(4)
        {
            double my_result = 0;
            double* result_ptr = (double*)omp2_area(0, 1, n, global_val);
            my_result += *result_ptr;
            # pragma omp critical
            *global_val += my_result;
            free(result_ptr);
        }
    }
    else if (algorithm_type == 3) {
        cout<<"OMP3:"<<endl;
        double temp = 0;
        gettimeofday(&start, NULL);
        # pragma omp parallel num_threads(4) reduction(+:temp)
        {
            // 归约操作中只有指定的共享变量才会被称为临界区, 其余的共享变量会乱序
            // 编译器会保证结果的正确性
            double* result_ptr = (double*)omp2_area(0, 1, n, global_val);
            temp += *result_ptr;
            // *global_val = temp;  *global_val不是指定的共享变量, 最后结果会出错
        }
        *global_val = temp;
    }
    else if (algorithm_type == 4) {
        cout<<"OMP4:"<<endl;
        gettimeofday(&start, NULL);
        omp3_area(0, 1, n, global_val);
    }
    else if (algorithm_type == 5) {
        cout<<"PThread:"<<endl;
        mutex1 = (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER;
        pthread_t thread[4];
        thread_parm thread_parm[4];
        for (int i = 0; i < 4; i++) {
            thread_parm[i].a = 0;
            thread_parm[i].b = 1;
            thread_parm[i].thread_id = i;
            thread_parm[i].thread_num = 4;
            thread_parm[i].global_val = global_val;
            thread_parm[i].n = n;
        }
        gettimeofday(&start, NULL);
        for (int i = 0; i < 4; i++) {
            pthread_create(&thread[i], NULL, pthread_area, (void*)&thread_parm[i]);
        }
        for (int i = 0; i < 4; i++) {
            pthread_join(thread[i], nullptr);
        }
        pthread_mutex_destroy(&mutex1);
    }
    gettimeofday(&end, NULL);
    total = (1000000 * (end.tv_sec - start.tv_sec) +  (end.tv_usec - start.tv_usec)) / 1000;
    return total;
}

clang++ -Xpreprocessor -fopenmp -lomp main.cpp -o main

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_40996518/article/details/106116384
今日推荐