並行與分佈式計算
MPI
初始化
MPI_Init(&argc, &argv)
結束
MPI_Finalize()
MPI_COMM_WORLD
一個通信域,包含通信的所有進程
當前進程標識
MPI_Comm_rank(MPI_COMM_WORLD, &node);
通信域包含的進程數
MPI_Comm_size(MPI_COMM_WORLD, &size);
信息發送
MPI_Send(message, strlen(message), MPI_CHAR, 1, 99, MPI_COMM_WORLD);
1是目的進程標識
信息接收
MPI_Recv(message, strlen(message), MPI_CHAR, 0, 99, MPI_COMM_WORLD);
0是發送進程標識
#include"mpi.h"
int main(int argc, char **argv){
char message[20];
int myrank;
MPI_Init(&argc,&argv); // 要傳地址
MPI_Comm_rank(MPI_COMM_WORLD,&myrank);
if(myrank == 0){
strcy(message,"Hello, process 1");
MPI_Send(message, strlen(message), MPI_CHAR, 1, 99, MPI_COMM_WORLD);
}
else if(myrank == 1){
MPI_Recv(message, strlen(message), MPI_CHAR, 0, 99, MPI_COMM_WORLD);
printf("messge: %s",message);
}
MPI_Finalize();
}
錯誤退出
master(0或者最後一個)進程退出,其他進程堵塞
#include"mpi.h"
#include<stdio.h>
int main(int argc, char **argv){
int node, size,i;
int masternode = 0;
MPI_Init(&argc,&argv);
MPI_Comm_rank(MPI_COMM_WORLD,&node);
MPI_Comm_size(MPI_COMM_WORLD,&size);
for(i=1;i<argc;++i){
fprintf(stderr,"myid=%d, procs=%d, argv[%d]=%s\n",node,size,i,argv[i]);
if(argv[i]&&strcmp("lastmaster",argv[i])==0){//字符串相等
masternode=size-1;
}
}
if(node == masternode){
fprintf(stderr,"myid=%d is masternode Abort!\n",node);
MPI_Abort(MPI_COMM_WORLD,99);
}
else{
fprintf(stderr,"myid=%d is not masternode Barrier!\n",node);
MPI_Barrier(MPI_COMM_WORLD);
}
MPI_Finalize();
}
數據接力傳送
#include"mpi.h"
#include<stdio.h>
int main(int argc, char **argv){
int rank, size, value;
MPI_Status status;
MPI_Comm_rank(MPI_COMM_WORLD,&rank);
MPI_Comm_size(MPI_COMM_WORLD,&size);
do{
if(rank==0){
fprintf(stderr,"\nPlease give new value=");
scanf("%d",&value);
fprintf(stderr,"%d read <-<- (%d)\n",rank,value);
if(size>1){//進程數大於1
MPI_Send(&value,1,MPI_INT,rank+1,0,MPI_COMM_WORLD);
fprintf(stderr,"%d send (%d) ->-> %d\n",rank,value,rank+1);
}
}
else{
MPI_Recv(&value, 1, MPI_INT, rank-1,0, MPI_COMM_WORLD,&status);
fprintf(stderr,"%d receive(%d) <-<- %d\n",rank,value,rank-1);
if(rank<size-1){
MPI_Send(&value,1,MPI_INT,rank+1,0,MPI_COMM_WORLD);
fprintf(stderr,"%d send (%d)->-> %d\n",rank,value,rank+1);
}
}
MPI_Barrier(MPI_COMM_WORLD);//執行一下同步,讓所有進程執行完,使前後兩次輸入數據分隔開
}while(value>=0);
MPI_Finalize();
}
進程之間相互問候
#include"mpi.h"
#include<stdio.h>
#include<stdlib.h>
void Hello(void);
int main(int argc, char **argv){
int rank, option, namelen, size;
char processor_name[MPI_MAX_PROCESSOR_NAME];
MPI_Init(&argc,&argv);
MPI_Comm_rank(MPI_COMM_WORLD,&rank);
MPI_Comm_size(MPI_COMM_WORLD,&size);
if(size<2){
fprintf(stderr,"systest requires at least 2 processes");
MPI_Abort(MPI_COMM_WORLD,1);
}
MPI_Get_processor_name(processor_name,&namelen);
fpritnf(stderr,"Process %d is alive on %s\n",rank,processor_name);
MPI_Barrier(MPI_COMM_WORLD);
Hello();
MPI_Finalize();
}
void Hello(void){
int size, rank;
int type = 1;
int buffer[2], node;
MPI_Status status;
MPI_Comm_rank(MPI_COMM_WORLD,&rank);
MPI_Comm_size(MPI_COMM_WORLD,&size);
if(rank==0){ //0進程負責打印提示消息
printf("\nHello test from all to all\n");
fflush(stdout);
}
for(node=0; node<size; node++){
if(node!=rank){
buffer[0]=rank;//自身標識
buffer[1]=node;//被問候標識
MPI_Send(buffer,2,MPI_INT,node,type,MPI_COMM_WORLD);
MPI_Recv(buffer,2,MPI_INT,node,type,MPI_COMM_WORLD,&status);
if((buffer[0]!=rank)||(buffer[1]!=node)){
//不是問候自己的或者不是不是以被問候的身份問候自己
(void) fprintf(stderr, "Hello: %d!=%d or %d!=%d\n",buffer[0],node,buffer[1],rank);
printf("Mismatch on hello process ids; node = %d\n",node);
}
printf("Hello form %d to %d\n",rank,node);
fflush(stdout);
}
}
}
任意源和任意標識的使用
#include"mpi.h"
#include<stdio.h>
int main(int argc, char **argv){
int rank, size, buff[1];
MPI_Status status;
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD,&rank);
MPI_Comm_size(MPI_COMM_WORLD,&size);
if(rank==0){
for(int i=0;i<100*(size-1);++i){
MPI_Recv(buf,1,MPI_INT,MPI_ANY_SOURCE,MPI_ANY_TAG,MPI_COMM_WORLD,&status);
printf("Msg=%d from %d with tag%d\n",buf[0],status.MPI_SOURCE,status.MPI_TAG);
}
}
else{
for(int i=0;i<100;++i){
buf[0]=rank+i;
MPI_Send(buf,1,MPI_INT,0,i,MPI_COMM_WORLD);
}
}
MPI_Finalize();
}
第9章 不同通信模式
通信模式 | 發送 | 接收 |
---|---|---|
標準通信 | MPI_Send | MPI_Recv |
緩存通信 | MPI_Bsend | |
同步通信 | MPI_Ssend | |
就緒通信 | MPI_Rsend |
使用同步消息發送,發送進程分別以同步方式發送1個和4個數據,消息標識tag分別爲1和2,接收進程用標準接收操作接收,並檢查收到數據的個數
#include"mpi.h"
#include<stdio.h>
#define SIZE 10
static int src = 0, dest = 1;//源進程,目的進程
int main(int argc, char **argv){
int rank,size;
int flag,rval,i;
int act_size = 0;
int buffer[SIZE];
MPI_STATUS status, status1, status2;
int count1, count2;
MPI_Init(&argc,&argv);
MPI_Comm_rank(MPI_COMM_WORLD,&rank);
MPI_Comm_size(MPI_COMM_WORLD,&size);
if(size!=2){
fprintf(stderr,"*** This program uses exactly 2 processes! ***\n");
MPI_Abort(MPI_COMM_WORLD,1);
}
act_size = 5; // 最大消息長度
if(rank == src){ // 當前進程爲發送進程
act_size = 1;
MPI_Ssend(buffer, act_size, MPI_INT, dest, 1, MPI_COMM_WORLD);
fprintf(stderr,"MPI_Ssend %d data, tag = 1\n",act_size);
act_size = 4;
MPI_Ssend(buffer, act_size, MPI_INT, dest, 2, MPI_COMM_WORLD);
fprintf(stderr,"MPI_Ssend %d data, tag = 2\n",act_size);
}
else if(rank == dest){
MPI_Recv(buffer, act_size, MPI_INT, src, 1, MPI_COMM_WORLD, &status1);
MPI_Recv(buffer, act_size, MPI_INT, src, 2, MPI_COMM_WORLD, &status2);
// act_size 是最大消息長度
MPI_Get_count(&status1, MPI_INT, &count1);
fprintf(stderr,"receive %d data, tag = %d\n",count1,status1.MPI_TAG);
MPI_Get_count(&status2, MPI_INT, &count2);
fprintf(stderr,"receive %d data, tag = %d\n",count2,status2.MPI_TAG);
}
MPI_Finalize();
}
第13章 組通信MPI程序設計
廣播
MPI_Bcast(&value, 1, MPI_INT, 0, MPI_COMM_WORLD);
#include<stdio.h>
#include"mpi.h"
int main(int argc, char **argv){
int rank, value;
MPI_Init(&argc,&argv);
MPI_Comm_rank(MPI_COMM_WORLD,&rank);
do{
if(rank==0){
scanf("%d",&value);
}
MPI_Bcast(&value,1,MPI_INT,0,MPI_COMM_WORLD);
//如果當前是廣播進程就發送,如果不是就接收,根據進程標識Bcast自動處理
printf("Process %d got %d\n",rank,value);
}while(value>=0); // 所有進程共用value
MPI_Finalize();
}
收集
MPI_Gather(sendarray,100,MPI_INT,rbuf,rcounts,displs,MPI_INT,root,MPI_COMM_WORLD);
// rbuf 整型數組,接收消息緩存區起始地址
// rcounts 整型數組,每個進程接收的數據個數
// displs 整型數組,每個入口相對於rbuf的偏移
// root 接收進程的標識號
注意爲數組開闢空間
收集和散發都是對應位置的緩存區數據交流
散發
MPI_Scatter(sendarray,100,MPI_INT,rbuf,100,MPI_INT,root,MPI_COMM_WORLD);
// root 發送進程的標識號
組收集
MPI_Allgather(sendarray,100,MPI_INT,rbuf,100,MPI_INT,MPI_COMM_WORLD);
第17章 共享存儲編程
使程序並行化
double area, pi,x;
int i, n;
area = 0.0;
#program omp parallel for private(x)
for (i = 0; i < n; ++i){
x = (i+0.5)/n;
#pragma omp critical // 生成一個代碼臨界區,使執行該段代碼的線程互斥
area += 4.0/(1.0 + x*x);
}
pi = area / n;
歸約操作
//歸約操作
// 負責將部分和存儲到私有變量中並在循環結束時將各個部分和相加等具體的操作
reduction(<op>:<variable>)
area = 0.0;
#pragma opm parallel for private(x) reduction(+:area)
for(i=0;i<n;++i){
x = (i+0.5)/n;
area += 4.0 / (1.0 + x*x);
}
pi = area / n;
名詞解釋
並行計算
並行計算是相對於串行計算來說的,可分爲時間上的並行和空間上的並行。時間上的並行指的就是流水線技術,而空間上的並行則是指用多個處理器併發的執行計算。並行計算的目的就是提供單處理器無法提供的性能,使用多處理器求解單個問題。
分佈式計算
分佈式計算研究如何把一個需要非常巨大的計算能力才能解決的問題分成許多小的部分,然後把這些部分分配個許多計算機進行處理,最後把這些計算結果綜合起來得到最終的結果。
並行計算機
並行計算機即能在同一時間執行多條指令或處理多個數據的計算機,並行計算機是並行計算的物理載體。
什麼是MPI
- MPI是一個庫,而不是一門語言
- MPI是一種標準或規範的代表,而不特指某一個對它的具體實現
- MPI是一種消息傳遞編程模型,併成爲這種編程模型的代表和事實上的標準
Hadoop
Hadoop是Apache軟件基金會旗下的一個開源分佈式計算平臺,爲用戶提供了系統底層細節透明的分佈式基礎框架
Hadoop的核心是HDFS和MapReduce,HDFS爲海量的數據提供了存儲,而MapReduce爲海量的數據提供了計算。
分佈式文件系統&計算機集羣
分佈式文件系統把文件分佈存儲到多個計算機節點上,成千上萬的計算機節點構成計算機集羣。
HDFS
Hadoop Distributed File System
Hadoop 分佈式文件系統,是指被設計成適合運行在通用硬件上的分佈式文件系統
- 兼容廉價的硬件設備
- 流數據讀寫
- 超大數據集
- 簡單的文件模型
- 強大的跨平臺兼容性
HDFS只設置了一個名稱節點(主節點)
數據節點
數據節點是分佈式文件系統HDFS的工作節點,負責數據的存儲和讀取,會根據客戶端或者是名稱節點的調度來進行數據的存儲和檢索,並向名稱節點定期發送自己所存儲的塊的列表。
每個數據節點中的數據會被保存在各自節點的本地linux文件系統中。
BigTable
Bigtable是一個分佈式存儲系統,起初用於解決典型的互聯網搜索問題。
HBase
HBase是一個高可靠、高性能、面向列、可伸縮的分佈式數據庫,是谷歌BigTable的開源實現,主要用來存儲非結構化和半結構化的鬆散數據,HBase的目標是處理非常龐大的表,可以通過水平擴展的方式,利用連接計算機集羣處理有超過10億行數據和數百萬列元素組成的數據表。
Zookeeper
Zookeeper是一個分佈式的,開發碼源的分佈式應用程序協調服務,是Hadoop和HBase的重要組件。
摩爾定律
CPU性能大約每隔18個月翻一番
從2005年開始摩爾定律逐漸失效
MapReduce
一種編程模型,將複雜的,運行與大規模集羣上的並行計算過程高度地抽象到了兩個函數:Map(映射)和Reduce(歸約)。
採用分治的思想,讓“計算向數據靠攏”,而不是“數據向計算靠攏”(挪動數據需要大量網絡開銷)
簡答題
解決節點運行期間EditLog不斷變大的問題
第二名稱節點是HDFS架構中的一個重要組成部分,他是用來保存名稱節點中對HDFS元數據信息的備份,並減少名稱節點重啓的時間。一般單獨運行在一臺機器上。
HDFS體系結構的侷限性
- 命名空間的侷限
- 性能的瓶頸
- 隔離問題
- 集羣的可用性:一旦名稱節點發生故障,整個集羣不可用
Hadoop已經有HDFS和MapReduce,爲什麼需要HBase
Hadoop可以很好地解決大規模數據的離線批量處理問題,但是受限於Hadoop MapReduce編程框架的高延遲數據初級機制,是得Hadoop無法滿足大規模數據試試處理應用的需求。
HBase和傳統關係型數據庫的比較
- 數據類型: HBase使用更簡單的數據模型,它把數據存儲爲未經解釋的字符串
- 數據操作:HBase在設計上就避免了複雜度表和表的關係,因此操作簡單
- 存儲模式:關係型數據庫基於行存儲,HBase基於列存儲,每個列族都由幾個文件保存,不同列族的文件是分離的
- 數據索引:關係型數據庫可以針對不同列構建複雜的多個索引,以提高數據訪問性能、HBase只有行鍵一個索引,使得整個系統很快。
- 數據維護:在關係數據庫中,更新操作爲替換操作,舊值從此消失。而HBase不會刪除原來的版本,而是產生新版本。
- 可伸縮性:關係型數據庫很難實現擴展,HBase就是爲了靈活的水平擴展而開發的,能夠輕易地通過在集羣中增加或者減少硬件數量實現性能的伸縮。
MapReduce相較於傳統的並行計算框架有什麼優勢
傳統並行計算框架 | MapReduce | |
---|---|---|
集羣架構/容錯性 | 共享式(共享內存/共享存儲),容錯性差 | 非共享式,容錯性好 |
硬件/價格/擴展性 | 刀片服務器、高速網、SAN,價格貴,擴展性差 | 普通PC機,便宜,擴展性好 |
編程/學習難度 | what-how,難 | what,簡單 |
適用場景 | 實時、細粒度計算、計算密集型 | 批處理、非實時、數據密集型 |