任务并行与数据并行
任务并行:将许多可以解决问题的任务分割,然后分布在一个或者多个核上进行程序的执行。
数据并行:将可以解决问题的数据进行分割,将分割好的数据放在一个或者多个核上进行执行;每一个核对这些数据都进行类似的操作。
冯诺依曼体系结构的瓶颈以及改进
瓶颈:CPU与主存的分离(可以比喻为负责生产的工厂与仓库的分离)
改进(1)CPU缓存(cache)
(2)虚拟存储器
(3)低层次并行
cache的特点
(1)内存位置集合,可以在比其它存储器有更短的访问开销。
(2)CPU缓存通常与CPU位于同一芯片上,访存开销比普通内存更快
cache的级别
cache可以有多级,第一级cache的访问速度比较快,但是容量比较小,第二级cache的速度比较慢,但是容量较大,向下逐渐如此
cache缺失与cache命中
当cpu需要访问主存或者数据时,他会沿着cache层次进行向下查询,首先是L1cache,再是L2cache,如果cache没有需要的信息就会访问主存,当向cache查询数据时候,cache中有数据就称为cache命中,如果不存在,就则称cache缺失
指令并行通过让多个处理器部件或者功能单位同时执行指令来提高处理器的性能,主要有两种方式:流水线和多发射
流水线:流水线:功能单元是分阶段安排。具体例子就是加法问题
多发:多个指令同时启动执行
多发射技术:多发处理器复制计算单元,同时执行程序中的不同指令.就比如说下面第一个加法器计算计算在z[1]的时候,第二个加法器就计算z[2],以此类推。
当功能单元在编译时被调度的就称为静态多发射
当功能单元在运行时被调度就称为动态多发射一个支持动态都多发射的处理器称为超标量
系统要找出能同时执行的指令,这种技术称为预测。
通常指令并行是很难利用的,因为很多程序之间都存在依赖性,比如说递归程序,所以就引入了硬件多线程。硬件多线程提供了一种方法,当前正在执行的任务已经停止时,系统可以继续做其他有用的工作。
cache一致性
基于目录的 Cache 一致性
使用一种称为目录的数据结构,该目录存储每个Cache line的状态.当一个变量被更新时,就会查询目录,并将所有包含该变量的cache line置为非法
监听 Cache 一致性
多个核共享总线.
在总线上传输的任何信号都可以被连接到总
线的所有核“看”到.
当core 0更新存储在其Cache中的x的副本
时,它也会通过总线广播该信息.
如果core1正在“监听”总线,它将知道x已
被更新,它将自己Cache中的x副本标记为
非法的
加速比的计算
核数 = p
串行程序运行时间= Tserial
并行程序运行时间= Tparallel
Tparallel = Tserial / p
加速比Y=Tparallel / Tserial
效率:
阿姆达尔定律
除非所有的串行程序都能够并行,否则无论可用的核的数量再多,加速将非常有限
可扩展性
粗略地讲,如果一个技术可以处理规模不断增加的问题,那么它就是可扩展的如果在增加进程/线程的数量时,可以维持固定的效率,却不增加问题规模,那么程序称为强可扩展(strongly scalable)
如果在增加进程/线程数量的同时,只有以相同倍率增加问题规模才能使效率值保持不变,那么程序就称为弱可扩展的(weakly scalable)
动态线程
主线程等待任务,fork新线程,当新线程完成时任务后,结束新线程资源的有效使用,但是线程的创建和终止非常耗时.
静态线程
创建线程池并分配任务,但线程不被终止直到被清理.性能更好,但可能会浪费系统资源
flynn的分类以及特点
单指令流单数据流(SISD)——传统的计算机包含单个CPU,它从存储在内存中的程序那里获得指令,并作用于单一的数据流
单指令流多数据流(SIMD)——单个的指令流作用于多于一个的数据流上。就被称为单指令流多数据流。SIMD的一个例子就是一个数组或向量处理系统,
优点
通过在处理器之间划分数据而实现的并行性将相同的指令应用于多个数据项它可以对不同的数据并行执行相同的操作。
缺点
所有ALU都需要执行相同的指令,或者保持空闲状态.在经典设计中,它们还必须同步运行.ALU不能进行指令存储. 对于大型数据并行问题有效,但对于其它更复杂的并行问题无效
多指令流单数据流(MISD)——用多个指令作用于单个数据流的情况实际上很少见。
多指令流多数据流(MIMD)——支持多个数据流上同时运行多个指令流. 通常由一组完全独立的处理单元或核心组成
,每一个核心都有自己的控制单元(CU)
和计算单元(ALU)
下面是几种编程模式的常用API
int MPI_Reduce(
void *input_data, /*指向发送消息的内存块的指针 */
void *output_data, /*指向接收(输出)消息的内存块的指针 */
int count,/*数据量*/
MPI_Datatype datatype,/*数据类型*/
MPI_Op operator,/*规约操作*/
int dest,/*要接收(输出)消息的进程的进程号*/
MPI_Comm comm);/*通信器,指定通信范围*/
int MPI_Send(
void* msg_buf_p, /* IN */
int msg_size, /* IN */
MPI_Datatype msg_type, /* IN */
int dest, /* IN */
int tag, /* IN */
MPI_Comm communicator /* IN */
);
int MPI_Recv(
void* msg_buf_p, /* msg_buf_p指向内存块 */
int buf_size, /* I存储对象的数量*/
MPI_Datatype buf_type, /*数据类型 */
int source, /* 从哪个进程发来的消息 */
int tag, /* tag需要与发送消息的参数tag相匹配 */
MPI_Comm communicator, /* 需要与发送进程的通信子匹配 */
MPI_Status* status_p /* OUT */
);
MPI_Sendrecv( void *sendbuf //initial address of send buffer
int sendcount //number of entries to send
MPI_Datatype sendtype //type of entries in send buffer
int dest //rank of destination
int sendtag //send tag
void *recvbuf //initial address of receive buffer
int recvcount //max number of entries to receive
MPI_Datatype recvtype //type of entries in receive buffer (这里数目是按实数的数目,若数据类型为
MPI_BCAST(void* data_p,int count,datatype,source_proc,comm)
IN/OUT data_p t通过data_p所引用的内容将通信子发送给所有进程
IN count 通信消息缓冲区中的数据个数(整型)
IN datatype 通信消息缓冲区中的数据类型(句柄)
IN source_proc 发送广播的进程的序列号(整型)
IN comm 通信子(句柄)
MPI_Scatter(
void* send_data,//存储在0号进程的数据,array
int send_count,//具体需要给每个进程发送的数据的个数
//如果send_count为1,那么每个进程接收1个数据;如果为2,那么每个进程接收2个数据
MPI_Datatype send_datatype,//发送数据的类型
void* recv_data,//接收缓存,缓存 recv_count个数据
int recv_count,
MPI_Datatype recv_datatype,
int root,//root进程的编号
MPI_Comm communicator)
int pthread_create(pthread_t *tidp, const pthread_attr_t *attr,
( void *)(*start_rtn)( void *), void *arg);
第一个参数为指向线程 标识符的 指针。
第二个参数用来设置线程属性。
第三个参数是线程运行函数的起始地址。
最后一个参数是运行函数的参数。```