并行程序设计心得

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

1、MPI实质上是多进程,并且不支持多线程;
2、初始化时在每个处理机(结点)上创建一个进程,执行期间进程数不能动态改变;
3、MPI其实就是一种在各进程之间进行通讯的标准;
4、并行程序设计最常用的两种方法:
1、共享变量方法——利用共享存储器中共享变量来实现处理机间的通信,它主要用在共享存储结构中(OpenMP)
2、消息传递方法——主要特点是不同处理机间必须通过网络传送消息来相互通信,并达到任务间需要的同步操作,它主要应用于分布式存储结构中(MPI)
5、一个基本的MPI 程序的流程图
对于多核程序设计的一点总结
多核下的多线程程序设计与传统的单核下的多线程程序设计有着一定的差别,在单CPU下,是多个线程在同一个CPU上并发地执行,而在多核下,则是由多个线程在多个核上并行地执行。

但是,目前的程序设计中对于多核的利用并没有达到预期的效果。

因此,为了能够在多核的环境下设计出更适合多核系统的程序,则是一个挑战。

要做到这一点,就必须将应用程序看做是众多相互依赖的任务的集合,将应用程序划分成多个独立的任务,并确定这些任务之间的相互依赖关系,这就称为分解(decomposition)。

如下如示:
分解方式设计说明
任务分解不同的程序行为采用不同的线程实现常用于GUI应用程序
数据分解多个线程对不同的数据块执行相同的操作常用于音频、图像处理和科学计算应用程序
数据流分解一个线程的输出作为另一个线程的输入应注意尽量避免延迟
任务分解:对应用程序根据其执行的功能进行分解的过程称为任务分解
(task decomposition)。

根据这种方法,就能够对众多的独立任务进行分类。

如果其中两个任务能够同时运行,那么开发人员就应该对其进行调度,形成二者之间的并行执行。

数据分解:数据分解也称为数据级并行(data-level parallelism)。

是将应用程序根据各任务所处理的数据而非按任务来进行分解的方法,即以数据为中心。

一般而言,能够按照数据分
解方式进行分解的应用程序都包含多个线程,这些线程分别对不同的数据对象执行相同的操作。

数据流分解:在很多情况下,当对一个问题进行分解时,关键问题不在于采用一些什么任务来完成这个工作,而在于数据在这些任务之间是如何流动的。

这个时候就要采用数据流分解方式,如典型的生产者/消费者问题。

对于任务分解,有两个需要注意的地方:
1. 划分的对象是计算,将计算划分为不同的任务
2. 划分后,研究不同任务所需的数据,如果这些数据不相交,则证明划分是成功的,如果数据有相当程序的相交,意味着要重新进行数据划分和功能划分。

如在一个气候模型中,需要考虑到地表模型、水文模型与海洋模型等多种情况,那么就应该将这几种模型划分成不同的任务,再分别进行并行地运算。

对于数据分解,划分的对象是数据,可以是算法的输入数据、中间处理数据和输出数据。

在划分时应该考虑到数据上的相应操作。

最简单的情况下,比如对1000万个数进行相加,可以对其进行数据上的分解,用多个线程来分别计算不同批次的数据。

然后再将计算的中间数据加到一起成为最终的结果。

1. 线程过多
线程并不是越多越好,对于某个程序,如果线程过多反而会严重地降低程序的性能。

这种影响主要在于:
将给定的工作量划分给过多的线程会造成每个线程的工作量过少,因此可能导致线程启动和终止的开销比程序实际工作的时间还要多。

同时,太多并发线程的存在将导致共享有限硬件资源的开销增大。

如保存和恢复寄存器状态的开销、保存和恢复线程cache状态的开销、废除虚存的开销、线程聚集在一起等待获取某个锁。

怎样解决这个问题:
限制可运行线程的个数
将计算线程与I/O线程分离开
使用已有的技术,如OpenMP,线程池等
2. 数据竞争、死锁
死锁,想必大家都很熟悉。

这里谈谈一些简单的避免死锁的规则:
加锁的顺序是关键,使用嵌套的锁时必须保证以相同的顺序获取锁
防止发生饥饿:即这个代码的执行是否一定会结束。

不要重复请求同一个锁
越复杂的加锁方案越有可能造成死锁—设计应力求简单
3. 竞争激烈的锁
即使是正确使用锁来避免数据竞争,但如果各线程对锁的竞争很激烈,那么也会引发性能问题。

即很多个线程来竞争同一个锁。

在这个情况下,可以考虑将资源划分成若干个部分,然后用彼此独立的锁分别保护各个部分。

即合理地安排加锁粒度的问题。

在早期的Linux内核
中就使用了大内核锁,现在已经把锁给细划到不同的子系统中去了。

如果一个数据结构被频繁读取但不被频繁写入,就可以使用读写锁来解决竞争。

4. 线程安全函数
已有的一些函数并不是线程安全的,具体的线程安全函数可以参考APUE2上面的线程一章。

5. 存储效率问题
目前,处理器的速度已经比存储器快很多,一个处理器从内存中读或写一个值的时间内可以完成上百次的操作。

因此,现在的程序往往受限于存储器的瓶颈。

首先,可以通过节省带宽,可以减少与存储器的数据交换数量,要节省带宽就要将数据压缩得更加紧凑。

其次是cache的利用。

可以减少数据在不同CPU的cache间的移动。

CPU亲合力(CPU Affinity)就是指在Linux系统中能够将一个或多个进程绑定到一个或多个处理器上运行。

一个进程的CPU亲合力掩码决定了该进程将在哪个或哪几个CPU上运行,在一个多处理器系统中,设置CPU亲合力的掩码可能会获得更好的性能。

在下面的例子中讲解了如何利用将某个进程绑定以某个CPU上运行的实例。

/bbs/viewthread.php?tid=904906&extra=page%3D1%26amp%3Bfilter% 3Ddigest
目前双核心的CPU 当道,AMD 的Athlon64x2、Intel 的Pentium-D、Core Duo,以及即将上市的Core 2 Duo,俨然将成为下一代电脑的主流(尤其是超低价的Pentium
D,绝对是现阶段C/P 值极高的双核心CPU)。

但是双核心有什麼用呢?
对於一般单一执行绪(single thread)的程式,多核心的处理器并没有办法提升它的处
理效能;不过对於多执行绪(multi thread)的程式,就可以透过不同的核心同时计算,来
达到加速的目的了!简单的例子,以单执行绪的程式来说,一件事做一次要十秒的话,要做
十次,都丢给同一颗核心做的话,自然就是10 秒* 10 次,也就是100 秒了;但是以多
执行绪的程式来说,它可以把这一件事,分给两颗核心各自做,每颗核心各做5 次,所以
所需要的时间就只需要50 秒!
OpenMP
☐诞生于1997年
☐目前正在制定并即将推出OpenMP 3.0版本

☐标准版本2.5,2005年5月,支持Fortran/C/C++
☐面向共享内存以及分布式共享内存的多处理器多线程并行编程语言
☐一种能够被用于显式指导多线程、共享内存并行的应用程序编程接口(API)
☐具有良好的可移植性,支持多种编程语言
☐支持多种平台
☐优点是简单、通用,有利于快速开发并行程序
⏹大多数的类UNIX系统以及Windows NT系统(Windows 2000,Windows XP,
Windows Vista等)
从物理划分上共享内存和分布式内存是两种基本的并行计算机存储方式除此之外
分布式共享内存也是一种越来越重要的并行计算机存储方式;
☐ 共享内存多处理器
⏹ 内存是共享的,某一个处理器写入内存的数据会立刻被其它处理
器访问到
☐ 分布式内存
⏹ 每一个处理器或者一组处理器有一个自己私有的内存单元
⏹ 共享或者不共享一个公用的内存单元
目前业界流行的共享内存模型开发标准是OpenMP 。

OpenMP 定义了一套编译指导语句,用于指定程序的并行性、数据的共享/私有等信息。

其目标是为SMP 系统提供可移植、可扩展的开发接口。

Intel ,DEC ,Silicon Graphics ,Kuch & Associates 和IBM 早在15 年前就联合定义了OpenMP 早期标准。

新的OpenMP 标准由OpenMP Architecture Review Board 于1997 年推出,现在已发展到2.0 版。

作为一套可移植可扩展的标准OpenMP 为程序员提供了一个简单和灵活的接口,可以方便地为共享内存的多处理器平台增加并行机制。

大部分高性能计算机厂商和软件厂商,例如。

OpenMP 在所有的架构上都支持使用C/C++FORTRAN 进行共享内存并行编程,包括基于Microsoft? WindowsNT? 和UNIX?操作系统的构架
OpenMP 还使用编译器指令和库函数,帮助并行应用程序员使用C/C++和FORTRAN 创建多线程应用。

对于包含有多个耗时的循环的应用,OpenMP 特别有用,它可以将工作划分为多个线程。

任一应用中划分粗糙的循环级别的并行机制的数量往往比较有限,限制了应用程序的可扩展性。

一个并行区域可能嵌入在其它并行区域之内,但是它们缺省的执行方式是必须使用一个线程组来串行执行。

OpenMP 使用fork- join 并行机制,程序首先顺序执行,然后转换成为并行程
序。

OpenMP 允许程序员使用划分良好的循环级并行机制来扩展应用,实现多处理。

它们可以添加划分粗糙的并行机制,同时仍然能保留以前在扩展方面所做的投资。

使用这种增量式的开发战略,程序员可以避免转向消息传递或其它并行编程模型时所具有的风险。

要开发新的应用,程序员必须分析原始问题,将它分解为多个使用共享和本地数据的任务,确定数据之间的依赖性,然后重新组织任务进入执行单元的顺序,这可以使用并行编程环境来实现编码。

展望:O penMP 将成为支持SMP 系统编程的主要标准,将来的工作在于研究和开发更加有效的OpenMP 编译器,以及更加强大友好的开发、调试工具。

处理器
原理:
❖以线程为基础,通过编译指导语句来显式地指导并行化,为编程人员提供了对并行化的完整的控制。

❖采用Fork-Join的形式
❖fork-Join执行模式
❖在开始执行的时候,只有主线程的运行线程存在
❖主线程在运行过程中,当遇到需要进行并行计算的时候,派生出(Fork,创建新线程或者唤醒已有线程)线程来执行并行任务
❖在并行执行的时候,主线程和派生线程共同工作
❖在并行代码结束执行后,派生线程退出或者挂起,不再工作,控制流程回到单独的主线程中(Join,即多线程的会和)。

❖OpenMP的功能
❖由两种形式提供
❖编译指导语句
❖运行时库函数
❖通过环境变量的方式灵活控制程序的运行
❖编译指导语句
☐在编译器编译程序的时候,会识别特定的注释
☐这些注释就包含着OpenMP程序的一些语义
#pragma omp <directive> [clause[ [,] clause]…]
其中directive部分就包含了具体的编译指导语句,包括parallel, for, parallel for, section, sections, single, master, critical, flush, ordered和atomic。

☐在无法识别OpenMP语义的普通编译器中,这些注释被忽略
☐将串行的程序逐步地改造成一个并行程序
❖运行时库函数
❖OpenMP运行时函数库原本用以设置和获取执行环境相关的信息,它们当中也包含一系列用以同步的API
❖支持运行时对并行环境的改变和优化,给编程人员足够的灵活性来控制运行时的程序运行状况。

❖OpenMP头文件omp.h
❖环境变量
❖最重要的环境变量是设置该OpenMP 执行时所需的线程总数,即OMP-NUM- THREADS.
例如,可以如下设置该环境变量:
❖setenv OMP-NUM- THREADS 16
❖即设置了16 个线程来运行该程序.。

相关文档
最新文档