篇外5、我的并行作业
发现上一篇关于一维流体力学统御方程组和热力学量的内容发布后竟然还能被评为优质内容,可能知乎还是更偏爱偏理论的内容?
为了避免误导别人,我还是说明一下这个专栏主要内容还是计算流体力学涉及到的一些编程内容,一些理论的内容主要还是为了帮助理解。毋庸置疑理论是核心、是编程的基础,但确实从控制方程到编程实现之间的距离还是很大的,我想若是能编程实现一个CFD算法,也能检验自己对知识的掌握和理解,毕竟CFD是帮助理解流体动力学本质的工具嘛。
这次这个篇外用来记录我本学期选修的其他学院的并行计算程序设计课的平时作业,续篇应当也是避免不了的。老师要求使用的是gcc编译器,并行计算框架为openmp或MPI。
还有别看前面都是配置环境这种“软”文,后面有一丢丢代码的嘤嘤嘤。
1、一些基本的配置工作
首先是编辑器
强烈建议使用VS,至于是VS2019还是2017倒是不太重要,不过如果还想使用fortran的话可能需要安装2015,查一下了解一下然后就下载吧。在浏览器上搜索VS,打开网址下载安装文件,安装VS2019。
下载community版本就好,足够用了。
如果只是使用C/C++,就不需要下载太多组件,这些可以了。至于python,咱用个pycharm或者notebook哪个不好?
之后是openmp
在VS中新建一个C++的控制台项目,之后在上面的工具栏中点击“项目”
在打开的菜单栏中最下端为本项目的属性设置,点开。
按如下打开小分支
分别把“openmp支持”和“运行库”选为和我一样的选项。
另外建议在下图这个位置把x86改成x64。
此外还有一个MPI的配置需要一些下载安装的操作,但是目前用的电脑不是自己的,所以就暂时先少折腾一点,之后再提吧。
2、关于并行的一丢丢介绍
并行计算又称为高性能计算,是指同时对多个任务、多条指令,或多个数据项进行处理。
摩尔定律:大概每两年,随着晶体管越来越小,在硅晶上的排布越加密集, 计算机的处理能力将会翻一翻,性能更好,成本更低。
并行计算被大量应用于计算密集、数据密集、网络密集领域。
并行编程范式包括相并行(phase parallel)、分治并行(divide and conquer parallel)、流水线并行(pipeline parallel)、主从并行(master-slave parallel)和工作池并行(work pool parallel)。再多的不说了,感兴趣去B站或者找PDF或者看超级计算机纪录片就可以了。
3、关于openmp的一丢丢介绍
OpenMP是由OpenMP Architecture Review Board牵头提出的,并已被广泛接受,用于 共享内存 并行系统的多处理器程序设计的一套指导性编译处理方案(Compiler Directive) [1] 。OpenMP支持的编程语言包括 C 、 C++ 和 Fortran ;而支持OpenMp的 编译器 包括Sun Compiler,GNU Compiler和Intel Compiler等。OpenMp提供了对 并行算法 的高层的抽象描述,程序员通过在 源代码 中加入专用的pragma来指明自己的意图,由此编译器可以自动将程序进行并行化,并在必要之处加入同步互斥以及通信。当选择忽略这些pragma,或者编译器不支持OpenMp时,程序又可退化为通常的程序(一般为 串行 ),代码仍然可以正常运作,只是不能利用多线程来加速程序执行。
openmp可以调用的线程一般不超过电脑的核数,比如我的小笔记本是8核16线程,一般用4或6或8个线程,如果使用得太多可能导致在信息传递上浪费太多时间,使得效率不升反降。
4、关于MPI的一丢丢介绍
MPI是一个跨语言的通讯协议,用于编写并行计算机。支持点对点和广播。MPI是一个信息传递应用程序接口,包括协议和和语义说明,他们指明其如何在各种实现中发挥其特性。MPI的目标是高性能,大规模性,和可移植性。MPI在今天仍为高性能计算的主要模型。与OpenMP并行程序不同,MPI是一种基于信息传递的并行编程技术。消息传递接口是一种编程接口标准,而不是一种具体的编程语言。简而言之,MPI标准定义了一组具有可移植性的编程接口。
5、每一篇不能没有的代码
这里当抛砖引石头,计算个 \Pi 吧,对圆周率的高精度计算也算是非常经典的一个多迭代计算案例了(不过这里还没有加上并行,意思意思,下个续篇开始放作业)。
\pi=\int_0^1 \frac{4}{1+x^2}dx\approx\sum_{0 \leq i \leq N}\frac{4}{1+(\frac{i+0.5}{N})^2}\cdot \frac{1}{N}
#include <iostream>
#include "stdio.h"
#define N 1000000
int main()
double local, pi = 0.0, w;
long i;
w = 1.0 / N;
for (i = 0; i < N; i++)
local = (i + 0.5) * w;
pi += 4.0 / (1.0 + local * local);
printf("pi is approximately %f\n", pi * w);
return 0;
}
对应的, \pi\approx3.1415926535 看作是真值,结果还不错。
PROGRAM PI
implicit none
real(kind=8) :: local
real(kind=8) :: pip = 0.0000000000
real(kind=8) :: w = 1.0000000000 / 1000000.0
integer(kind=8) :: i
real(kind=8) :: j = 0.0000000000
DO i = 1, 1000000