初识OpenMP 编写简单程序并评价其性能
摘要
最近导师在给我们讲授高性能计算的课程,然后布置了一个任务是学习OpenMP并会简单的编程,然后再测量其性能。
之前完全没有听过这个词,从维基百科上查阅了下,然后根据自己理解做了一些总结并进行了简单的程序测试及性能评估。供大家参考!
一、OpenMP介绍
定义
OpenMP是Open Multi-Processing的简称,维基上给出的定义:OpenMP是一套支持跨平台共享内存方式的多线程并发的编程API,使用C,C++和Fortran语言,可以在大多数的处理器体系和操作系统中运行。
它实际上就是一套便于开发的应用程序接口,程序员在编写代码时可以直接调用库函数中定义好的函数而不用自己再去编写,和其他大部分API是一样的,用哪个学哪个调用哪个就行,和我们查字典一样。
但这套API 的功能是针对共享内存并行系统的多线程程序设计而存在的,顾名思义它对于非共享内存系统(如计算机集群)无效,这也是OpenMP的一个缺点。
多线程及并行程序
然后再来说一下多线程,举一个很简单的例子,假设正值售票高峰期,只有一个窗口售票,售票员只能一个一个的服务,你去排队买票,你觉得你什么时候能买到?所以一般售票处窗口不止一个,多个窗口同时进行售票服务,这就是多线程的基本思想。同样的,对于计算机来说,单处理器的计算效能肯定不如多处理器,现如今双核、四核的 CPU 当道,而六核的CPU也已经面世多时,所以在多处理机上编写、运行并行程序会变得相当普遍。
我们通常所写的程序属于单一执行绪的程式(single thread 单线程),多核心的处理器并没有办法提升它的处理效能;不过对于多执行绪的程式(multi thread 多线程),就可以通过不同的内核同时计算,来达到加速的目的。简单的例子,以单线程来说,计算1+1=2要十秒的话,做十次,如果都丢给同一个内核,就是 10 秒 * 10 次,即 100 秒;而多线程,它可以把计算式分给两个cpu去做,每个各做 5 次,所以需要的时间就只有 50 秒! 我们可以把这种分配计算理解为并行处理,所写的程序即并行程序。
写程序的时候该怎么去写这样的程序呢?一般的方法,就是利用线程的控制,去产生多个 thread。由主线程把工作拆开,分给子线程去运算,最后再由主线程回收结果、整合。
OpenMP优势
但是,实际上要去控制线程是很麻烦的,在程序的编写上,也会复杂不少。而OpenMP是一个跨平台的多线程实现,它可以通过高阶指令,很简单地将程式并行化、多线程化;将主线程(顺序的执行指令)生成一系列的子线程,并将任务划分给这些子线程进行执行。这些子线程并行的运行,由运行时环境将线程分配给不同的处理器。假如我们只是想要把一些简单的循环并行化处理,最简单的情形,只加一行指令,就可以将循环内的程序并行化处理了!
了解了以上这些概念,不难理解OpenMP是什么样的API了,它是为在多处理机上编写并行程序而设计的一个应用编程接口。它提供了对并行算法的高层的抽象描述,降低了并行编程的难度和复杂度,这样程序员可以把更多的精力投入到并行算法本身,而非其具体实现细节。程序员只需通过在源代码中加入专用的pragma(OpenMP 的语法)来指明自己的意图,由此编译器可以自动将程序进行并行化,并在必要之处加入同步互斥以及通信。当选择忽略这些pragma,或者编译器不支持OpenMP时,程序又可退化为通常的程序(一般为串行),代码仍然可以正常运作,只是不能利用多线程来加速程序执行。
其它
OpenMP包括一套编译指导语句和一个用来支持它的函数库。
#pragma omp <directive> [clause[[,] clause] ...]
其中,directive共11个;clause13个;
OpenMP还定义了20多个库函数,具体用法在这里不一一列举了,大家可以直接在维基百科或者官网查看。
二、简单程序编写
OpenMP配置
我用的是visual studio 2019 ,打开程序——创建新项目
依次选择后下一步
项目名称——存储位置——创建
解决方案资源管理器——源文件右击——添加——新建项——c++文件——名称后缀改为.c——添加
项目——属性——c/c++——语言——openmp支持打开——应用确定
代码测试
将维基百科中Hello, world的例子稍微做了点改动。
1.首先测试串行程序,Hello, world打印100000次所花费的时间。
/*串行测试*/ #include <stdio.h> #include <stdlib.h> #include<time.h> int main(int argc, char* argv[]) { double t1, t2; t1 = clock(); for (int i = 0; i < 100000; i++) printf("Hello, world.\n"); t2 = clock(); printf("%fs", (t2 - t1) * 0.001); return 0; }
运行结果:
2.其次测试openmp的并行效果。这里注意:编写带编译指令的并行程序时,一定要加上<omp.h>头文件,OpenMP的函数都声明在头文件omp.h中。
/*openmp并行测试*/ #include<omp.h> #include <stdio.h> #include <stdlib.h> #include<time.h> int main(int argc, char* argv[]) { double t1, t2; int i; t1 = clock(); #pragma omp parallel for for (i = 0; i < 100000; i++) printf("Hello, world.\n"); t2 = clock(); printf("%fs", (t2 - t1) * 0.001); return 0; }
运行结果:
三、性能评价
通过以上实验结果,可以看到,串行和并行相差的时间并不是很多,我运行了至少有五十次,结果甚至是串行所用时间更短,预期的结果应该是多线程速度会有明显的提升,但在这里,不知道是循环代码过于简单,还是因为硬件的关系,因为电脑的是单核的。刚接触openmp,还是遇到了困难,希望有大神指点,互相学习!
参考