并行程序设计——OMP编程

article/2025/4/30 14:47:22

并行程序设计——OMP编程

实验一

实验内容

分别实现课件中的梯形积分法的Pthread、OpenMP版本,熟悉并掌握OpenMP编程方法,探讨两种编程方式的异同。

实验代码

OpenMP编程

#include <stdio.h>
#include <stdlib.h>
#include <omp.h>void Trap(double a, double b, int n, double *global_result_p);double f(double x) {return x * x;
}int main(int argc, char *argv[]) {double global_result = 0.0;double a, b;int n;int thread_count;thread_count = 5;printf("Enter a, b, and n\n");scanf("%lf %lf %d", &a, &b, &n);
# pragma omp parallel num_threads(thread_count)Trap(a, b, n, &global_result);printf("With n = %d trapezoids, our estimate\n", n);printf("of the integral from %f to %f = %.14e\n", a, b, global_result);return 0;
} /* main*/void Trap(double a, double b, int n, double *global_result_p) {double h, x, my_result;double local_a, local_b;int i, local_n;int my_rank = omp_get_thread_num();int thread_count = omp_get_num_threads();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.0;for (i = 1; i <= local_n; i++) {x = local_a + i * h;my_result += f(x);}my_result = my_result * h;
# pragma omp critical*global_result_p += my_result;
} /* Trap*/

实验效果

请添加图片描述

pthread编程

#include <pthread.h>
#include <algorithm>
#include <stdio.h>
# include <stdlib.h>double f(double x) {return x * x;
}typedef struct {int threadId;
} threadParm_t;
const int THREAD_NUM = 5; //表示线程的个数
int next_task = 1;//从1开始
int seg = 50;//每次分50份
double totalSize = 0.000; //表示总面积
double h = 0.00;//表示间距
double a = 1.000, b = 8.000;
int n = 1000;
pthread_mutex_t barrier_mutex = PTHREAD_MUTEX_INITIALIZER;void cal(int i) {//计算第i分double x = a + i * h;double myResult = f(x) * h;pthread_mutex_lock(&barrier_mutex);totalSize += myResult;pthread_mutex_unlock(&barrier_mutex);
}void *SSE_pthread(void *parm) {int task = 0;while (true) {pthread_mutex_lock(&barrier_mutex);next_task += seg;task = next_task;pthread_mutex_unlock(&barrier_mutex);if (task - seg >= n) break;for (int i = task - seg; i < std::min(task, n); i++) {cal(i);}}pthread_exit(nullptr);
}int main(int arg, char *argv[]) {pthread_t thread[THREAD_NUM];threadParm_t threadParm[THREAD_NUM];for (int i = 0; i < THREAD_NUM; i++) {threadParm[i].threadId = i;}printf("Enter a, b, and n\n");scanf("%lf %lf %d", &a, &b, &n);//先计算两边的面积之和h = (b - a) / n;totalSize = h * (f(a) + f(b)) / 2;for (int i = 0; i < THREAD_NUM; i++) {pthread_create(&thread[i], nullptr, SSE_pthread, (void *) &threadParm[i]);}for (int k = 0; k < THREAD_NUM; k++) {pthread_join(thread[k], nullptr);}pthread_mutex_destroy(&barrier_mutex);printf("With n = %d trapezoids, our estimate\n", n);printf("of the integral from %f to %f = %.14e\n", a, b, totalSize);return 0;
}

实验结果

请添加图片描述
注:由于我的pthread求梯形的基准和OpenMP的基准不同,所以求的实验结果略有差异

两种编程方式的异同

OpenMP是根植于编译器的,更偏向于将原来串行化的程序,通过加入一些适当的编译器指令(compiler directive)变成并行执行,从而提高代码运行的速率。这样做是在没有OpenMP支持编译时,代码仍然可以编译。 创建线程等后续工作需要编译器来完成。

Pthread是一个库,所有的并行线程创建都需要我们自己完成。Pthread仅在有多个处理器可用时才对并行化有效,并且仅在代码针对可用处理器数进行了优化时才有效。 因此,OpenMP的代码更易于扩展。

实验二

实验描述

对于课件中“多个数组排序”的任务不均衡案例进行OpenMP编程实现(规模可自己调整),并探索不同循环调度方案的优劣。提示:可从任务分块的大小、线程数的多少、静态动态多线程结合等方面进行尝试,探索规律。

实验代码

#include <stdio.h>
#include <stdlib.h>
#include <omp.h>
#include <ctime>
#include <sys/time.h>
#include <algorithm>const int ARR_NUM = 1000; //固定数组大小
const int ARR_LEN = 4000; //固定数组长度
int THREAD_NUM = 4; //固定线程个数,改变粗颗粒分配大小
int seg = 10; //改变粗颗粒的分配大小
struct timeval startTime, stopTime;// timers
int arr[ARR_NUM][ARR_LEN];
int tempArr[ARR_NUM][ARR_LEN]; //用于暂时存储数组,控制变量唯一
int next_arr = 0;void OMP_sort();void init(int num) {srand(unsigned(time(nullptr)));for (int i = 0; i < num; i++) {for (int j = 0; j < ARR_LEN; j++)arr[i][j] = rand();}
}//使用冒泡排序,使变量唯一
void sort(int *a) {for (int i = 0; i < ARR_LEN - 1; ++i) {for (int j = i + 1; j < ARR_LEN; ++j) {if (a[j] < a[i]) {int temp = a[i];a[i] = a[j];a[j] = temp;}}}
}void store(int a[][ARR_LEN], int b[][ARR_LEN], int num) {for (int i = 0; i < num; i++) {for (int j = 0; j < ARR_LEN; j++)b[i][j] = a[i][j];}
}int main(int argc, char *argv[]) {init(ARR_NUM);store(arr, tempArr, ARR_NUM);for (; seg <= 100; seg += 10) {printf("seg: %d\n",seg);store(tempArr, arr, ARR_NUM);next_arr = 0;gettimeofday(&startTime, NULL);
# pragma omp parallel num_threads(THREAD_NUM)OMP_sort();gettimeofday(&stopTime, NULL);double trans_mul_time =(stopTime.tv_sec - startTime.tv_sec) * 1000 + (stopTime.tv_usec - startTime.tv_usec) * 0.001;printf("time :%lf ms\n", trans_mul_time);}return 0;
} /* main*/void OMP_sort() {int task = 0;while (true) {
#pragma omp critical //每次只能一个线程执行task = next_arr += seg;//每次动态分配seg个if (task - seg >= ARR_NUM) break;int min = task < ARR_NUM ? task : ARR_NUM;for (int i = task - seg; i < min; ++i) {sort(arr[i]);}}
} /* Trap*/

实验运行结果

请添加图片描述
如此进行多次重复实验,将得到的数据绘成echarts图表

实验图表和结论

请添加图片描述

实验结论

  1. 由以上图表可知,根据动态粗颗粒的分配的大小可知,各个分配结果的运行效率基本相似。
  2. 由上图可知,当每次分配数目为30的时候,运行时间最短,运行效率最快。但是当每次分配个数超过80时,运行效率也不低。由于此次实验总的数组的行数比较少,所以运行效率并不会相差很大,如若需要更加准确的数值的话,则需要加大数组的行数。

实验三(附加题)

实验内容

实现高斯消去法解线性方程组的OpenMP编程,与SSE/AVX编程结合,并探索优化任务分配方法。

实验代码

#include <stdio.h>
#include <stdlib.h>
#include <omp.h>
#include <ctime>
#include <sys/time.h>
#include <algorithm>
#include <pmmintrin.h>const int n = 2048;//固定矩阵规模,控制变量
const int maxN = n + 1; // 矩阵的最大值
float a[maxN][maxN];
float temp[maxN][maxN];//用于暂时存储a数组中的变量,控制变量唯一
const int THREAD_NUM = 4; //表示线程的个数
int seg = 30;//表示每次线程运行的行数,将来它会改变来探究最优任务分配方法
int next_task = 0;
int line = 0;//记录当前所依赖的行数
struct timeval startTime, stopTime;// timers/*** 根据第i行的元素,消除j行的元素* @param i 根据的行数* @param j 要消元的行数*/
void OMP_elimination(int i, int j) {float tep;__m128 div, t1, t2, sub;// 用temp暂存相差的倍数tep = a[j][i] / a[i][i];// div全部用于存储temp,方便后面计算div = _mm_set1_ps(tep);//每四个一组进行计算,思想和串行类似int k = n - 3;for (; k >= i + 1; k -= 4) {t1 = _mm_loadu_ps(a[i] + k);t2 = _mm_loadu_ps(a[j] + k);sub = _mm_sub_ps(t2, _mm_mul_ps(t1, div));_mm_store_ss(a[j] + k, sub);}//处理剩余部分for (k += 3; k >= i + 1; --k) {a[j][k] -= a[i][k] * tep;}a[j][i] = 0.00;
}/*** 多线程消元函数,动态粗颗粒分配,每次分配seg个* @param parm*/
void OMP_func() {int task = 0;while (true) {
#pragma omp critical //每次只能一个线程执行{task = next_task;next_task += seg;//每次分配seg个}if (task >= n) break;int min = task + seg < n ? task + seg : n;for (int i = task; i < min; ++i) {OMP_elimination(line, i);}}
}//用于矩阵改变数值,为防止数据溢出,随机数的区间为100以内的浮点数
void change() {srand((unsigned) time(NULL));for (int i = 0; i < n; i++) {for (int j = 0; j <= n; j++) {a[i][j] = (float) (rand() % 10000) / 100.00;}}
}/*** 将a数组的数据存储到b数组中* @param a* @param b*/
void store(float a[][maxN], float b[][maxN]) {for (int i = 0; i < n; i++) {for (int j = 0; j <= n; j++) {b[i][j] = a[i][j];}}
}int main(int arg, char *argv[]) {change();store(a, temp);// SSE算法消元设计for (; seg <= 300; seg += 30) {store(temp, a);//从temp中取数printf("seg: %d\n", seg);gettimeofday(&startTime, NULL);for (line = 0; line < n - 1; ++line) {next_task = line + 1;
# pragma omp parallel num_threads(THREAD_NUM)OMP_func();}gettimeofday(&stopTime, NULL);double trans_mul_time =(stopTime.tv_sec - startTime.tv_sec) * 1000 + (stopTime.tv_usec - startTime.tv_usec) * 0.001;printf("time: %lf ms\n", trans_mul_time);}
}

代码运行结果

请添加图片描述

如此重复多次实验,将所得到的的数据绘成echarts图表

实验图表和结论

请添加图片描述
实验结论

  1. 与上个实验不同的是,这次实验我增加了整体矩阵的大小和每次分配行数的间距,这样得出的实验结果更加准确。
  2. 由上图表可知,当矩阵规模达到2048 × \times × 2048时,整体运行的运行时间都相对较短,运行效率相比于串行化执行要快很多。每次分配90行的时候运行效果最佳,180~240的时候与90相差不大,整体区别不太。所以矩阵规模越大,尽量分配越多的行数使运行效率加快。

http://chatgpt.dhexx.cn/article/fvwEUYcc.shtml

相关文章

利用中国知网快速自动生成参考文献

1.打开知网 2.输入引用的文献名称 3.点击文章 4.点击“导出/参考文献” 5.自动生成参考文献

知网导出外文参考文献格式和下载文章(2019.5)

如果不弹出页面&#xff0c;是网络的原因&#xff0c;等一等

谷歌学术、中国知网生成参考文献

谷歌学术 step1.登陆谷歌学术 step2.查找需要的文献 step3.点击引用标志 step4.生成相关引用 step5.选择不同的标准复制粘贴 中国知网 step1.在知网搜索需要的论文 step2.点击导出参考文献 step3.生成参考文献引用

中国知网文献引用导入EndNote9.X,Web of science导入endnote以及谷歌学术导入endnote图文详解,全网最细版本适用EndNote9.x,Endnote20版本

文章目录 一、EndNote导入文献的以下几种格式1.1 中国知网1.2web of science1.3 谷歌学术 一、EndNote导入文献的以下几种格式 all as we konow&#xff0c;引用参考文献也就是如下三个&#xff0c;那么分别导入endnote改怎么使用呢&#xff1f; 1.1 中国知网 在你想要的文献里…

在CNKI上导出TXT文件

在使用CiteSpace之前要先下载数据源&#xff0c;今天就来讲一讲从CNKI上导出txt文件。 1、从学校官网进入中国知网CNKI&#xff0c;单击高级检索 2、输入关键字&#xff0c;可以选择组合输入&#xff0c;单击搜索 3、在每页显示处选择50 4、勾选所有所有记录&#xff08;每次导…

EndNote20导入知网文献和导出BibTeX于TexStudio

第一步&#xff1a;在知网官网中找到一篇论文并点击引号键 第二步&#xff1a;点击EndNote下载该txt文件 第三步&#xff1a;在EndNote20中点击File->Import->File&#xff0c;引入刚刚下载的txt文件 注意Import Option选择的是EndNote Import&#xff0c;点击import导入…

知网导出之Excel

背景&#xff1a;需要整理知网XX方向的论文&#xff0c;包括题目&#xff0c;内容&#xff0c;发表时间等等信息。但是一个个写也太麻烦了&#xff0c;所以&#xff0c;一些不需要人总结的东西&#xff0c;就直接导出好了。 流程&#xff1a; 选择好文献之后&#xff0c;选择自…

知网如何快速引用参考文献

1.在知网搜索界面&#xff0c;搜索自己想搜的内容&#xff0c;然后点击下图引号位置 2.在弹框中选择你要引用的格式 复制粘贴到自己论文中即可

知网 BibTeX自动生成(使用BibTeX引用中文参考文献)

前言 谷歌学术具备生成英文文献的bibtex文献引用代码的功能&#xff0c;而知网里不具备生成中文文献的bibtex引用代码的功能。因此&#xff0c;本文将生成中文文献bibtex引用代码的操作过程简单记录便于自己再次翻阅&#xff0c;操作方法源自知乎作者 上官无忌 &#xff0c;具…

知网导出引用文件,插入到Endnote管理文献

直接选endnote是txt格式 改格式也没用&#xff0c;endnote改也不行 这里选择refworks 导出txt之后 选择refworks import即可

Web of Science如何导出参考文献

Web of Science&#xff08;WOS&#xff09;是大型综合性、多学科、核心期刊引文索引数据库。 Endnote是一款可以有效管理参考文献的软件&#xff0c;并且在论文写作时&#xff0c;利用endnote可以很方便的修改引用格式。 1.进入论文界面 2.点击查看PDF,然后点击红圈 3. 可以…

Endnote导出目标期刊的参考文献的格式

Endnote导出目标期刊的参考文献的格式 1、知网文献与endnote的操作流程2、其他的参考文件来源&#xff08;如bib、ris等&#xff09;及导出格式选择 1、知网文献与endnote的操作流程 导出的格式为txt格式&#xff0c;这时候打开endnote&#xff0c;导入choose你的txt文件就可以…

知网论文参考文献导入到Endnote方法

第一步下载参考文献文件第二步&#xff1a;右键->打开方式->选择endnote 3. 导入结果

知网获取论文参考文献

知网获取论文参考文献 进入知网搜索相应材料普通检索高级检索 选择相应的文献点击右上角左边双引号“凑”参考文献 进入知网 中国知网官方网址&#xff1a;https://www.cnki.net/ 搜索相应材料 搜素一般可分为普通检索和高级检索。 一般而言&#xff0c;普通检索即可完成我…

Endnote 导出英文、中文(知网)参考文献进入Word

1、英文文献 从Google Scholar 搜索需要的参考文献&#xff0c;然后点击“引用”按钮&#xff0c;导出Endnote的格式&#xff0c;例如scholar.enw。 在Endnote中File-->Import-->File...-->Import File-->Import 参考文献导入完毕 进行参考文献在word中的导出…

将CNKI知网文献条目导出,并导入到Endnote内

将CNKI知网文献条目导出&#xff0c;并导入到Endnote内 目录 将CNKI知网文献条目导出&#xff0c;并导入到Endnote内一、从知网上导出参考文献二、将知网导出的参考文献导入到Endnote 一、从知网上导出参考文献 从知网上导出参考文献过程和步骤如图1所示。 图1 导出的参考文献…

中文参考文献如何导出+如何插入参考文献

很多小白不清楚如何导出中英文的参考文献&#xff0c;如何插入参考文献 来源公众号【学术点滴】 小编写第一篇论文是踩过了两个坑&#xff1a; ①手工一个个编写的参考文献 ②在Word中采用最原始方式插入参考文献&#xff0c;当增加或删除一篇参考文献时&#xff0c;需要手…

LaTeX插入知网参考文献

方式1&#xff1a;直接使用bibtem 新建一个 .tex文档&#xff0c;加入以下语句 \begin{thebibliography}{1} \bibitem{RN1}唐志刚. 基于YOLO V3的航拍车辆图像检测方法研究[D].江西理工大学,2020. \end{thebibliography} RN1是引用名&#xff0c;自定义&#xff1b;后面直接粘…

指令重排详解

指令重排&#xff1a; 编译器指令重排&#xff0c;cpu指令重排&#xff0c;内存指令重排。 编译器可能会调整顺序&#xff0c;如下图&#xff0c;左边是c源码&#xff0c;右边是优化后顺序 一条汇编指令的执行是可以分为很多步骤的, 分为不同的硬件执行 取指 IF 译码和取寄存…

指令重排的原理

指令重排 一. 指令重排的分类&#xff1a; 编译器优化的重排指令级并行的重排内存系统的重排 编译器优化重排序和指令级并行重排序不会影响程序的结果&#xff0c;因此程序员需要着重处理的是内存系统重排序带来的问题。 二. 指令重排的原理&#xff1a; 1.编译器优化的重…