【1】Embarrassingly Parallel(易平行計算問題)

aalanwyr發表於2021-12-09

1、什麼是Embarrassingly Parallel(易平行計算問題)

       易平行計算問題:A computation that can be divided into a number of  completely independent tasks。在編寫並行程式過程中,首先需要將一個問題分解成若干部分,然後將每個部分分配給不同的processer(處理器)或者thread(執行緒)分別進行計算,如果該計算問題能夠被分解成一些完全獨立的子計算(task)、同時各個task之間資料幾乎沒有依賴,沒有通訊。那這個計算問題就叫作易平行計算問題。

      對於該問題一般程式處理流程如下:

     對於輸入的Data,直接拆分成幾塊互相沒有依賴的Subdata,每一份Subdata具有完全相同的處理方式,最後再將subdata的處理結果合併行輸出,對於這類資料計算問題,就天然的適用於平行計算。尤其是在影像處理方面,並行處理的情況很多。

      在對影像進行平移、旋轉、縮放過程中,對於每一個pixel(畫素點)資料都是完全相同的操作,當然我們可以寫一個兩層for迴圈,遍歷每個pixel,然後對每個pixel進行轉換,但這樣效率很低。按照並行程式設計的思路,我們可以對一幅影像不同的畫素點進行拆分,送到不同processer,同時進行影像計算,這樣就實現了影像計算的加速。理想情況下,對於每個piexl,我們們都送到一個processer中進行計算,這樣效率最高。但實際情況是,往往processer或者thread開啟的數量也是有限度的,就像高速公路分流一樣,不可能無限制的擴寬車道,所以我們們需要對一整個Data進行合理的拆分,將一塊資料送入processer中進行處理。正如上圖所示,對於原始影像的拆分可以分塊、分行、分列,不同的分割方式對最後執行效率也是有一定影響的。

2、易平行計算需要考慮的問題

    對於易平行計算問題,雖然資料很容易進行拆分處理,但在實際編寫程式過程中往往會遇到兩個問題,首先我們看一下並行程式設計的程式框架。

    對於影像的平移處理,一般並行程式由主從結構構成,主程式用來對資料進行拆分,送到不同的process中,並接受不同process的返回值。

   

?主程式

//master process 對於480*640的影像 
for(i=0, row=0; i<48; i++, row+=10) // for each of 48 processes 按行拆分 
{
	send(row, Pi); // send row no. 
} //資料拆分 以及傳送
for(i=0; i<480; i++){
  for(j=0; j<640; j++) {
    temp_map[i][j] = 0; // initialize temp 緩衝陣列
  }
}
for(i=0; i<(480*640); i++) { // for each pixel
	recv(oldrow, oldcol, newrow, newcol, PANY); // accept new coordinates 資料接收
	if !((newrow<0)||((newrow>=480)||(newcol<0)||((newcol>=640)){
		temp_map[newrow][newcol] = map[oldrow][oldcol];
    }
}
for(i=0; i<480; i++){
  for(j=0; j<640; j++) {
    map[i][j] = temp_map[i][j]; // update map 更新影像
  }
}

 ?子程式

// slave process
recv (row, Pmaster);
for (oldrow = row; oldrow < (row+10); oldrow++) // for each row in the partition 區域性線性處理
{
	for (oldcol = 0; oldcol < 640; oldcol++) { // for each column in the row 
		newrow = oldrow + delta_x; // shift along x-dimension
		newcol = oldcol + delta_y; // shift along y-dimension
		send(oldrow, oldcol, newrow, newcol, Pmaster); // send out new coordinates
	}
}

     上述程式框架就是完全按照主從結構進行編寫的,我們可以發現,在master process中我們需要完成資料拆分、傳送、接收。在slave process中我們需要完成資料計算(對於embarrassingly parallel來說這部分相對簡單)。

    按照上述程式框架我們需要在master process中考慮:一、資料如何拆分;二、各個subdata如何合理的分配到不同的processer( Load Balancing 負載均衡);因為往往並行程式的執行時間是由執行時間最長的process決定的,所以儘量讓每個processer平均高效的完成工作才是最重要的。

3、易並行程式的優化

   在給processer分配任務過程中,主要分為兩大類: Static Load-Balancing (靜態分配)、 Dynamic Load Balancing (動態分配)。  

3.1、Static Load-Balancing (靜態分配)

    常見的靜態分配有 1、Round robin algorithm — selects processes in turn   2、 Randomized algorithms — selects processes randomly 

    靜態分配就如上面這個程式處理邏輯一樣,在master中就將subdata分割好,每個processer中傳入的subdata是可以確定的,對於簡單的問題這樣分配任務是沒問題的,但是對於複雜的問題(尤其是不同的subdata執行的時間不同),那這樣靜態分配會導致有些processer特別忙、有些processer又處於空閒,導致如下的時間分配問題:

         就像單位用人一樣,相同的任務量不同的人完成的時間不同,需要根據能力分配任務,能者多勞,照顧新人。

3.2、Dynamic Load Balancing (動態分配)

        一般來說對於複雜並行處理問題,尤其是無法確定slave process處理時間的問題,都需要用到動態分配。常見的動態分配演算法有:1、 Centralized Work Pool ;2、 Decentralized Work Pool ;3、Fully Distributed Work Pool 。

   3.2.1 Centralized Work Pool(工作池)

      先上程式框架:

?主程式

//master process
count = 0; // # of active processes
row = 0; // row being sent
for (i=0; i<num_proc; i++) { // send initial row to each processes
	send(row, Pi , data_tag); //先輪流送給每一個processer
	count++;
	row++;
}
do {
	recv(&slave, &r, color, PANY , result_tag); //收到某個執行緒完成的工作結果
	count--;
	if (row < num_row) { // keep sending until no new task 如果row還沒完成
		send(row, Pslave , data_tag); // send next row  傳送下一行資料
		count++;
		row++;
	} 
  	else {
		send(row, Pslave , terminate_tag); // terminate 讓這個process終止
	}
	display(r, color); // display row
} while(count > 0);

?從程式

//slave process P ( i )
recv(&row, Pmaster , source_tag);
while (source_tag == data_tag) { // keep receiving new task
	c.imag = min_imag + (row * scale_image);
	for (x=0; x<640; x++) {
		c.real = min_real + (x * scale_real);
		color[x] = cal_pixel (c); // compute color of a single row
	}
	send(i, row, color, Pmaster , result_tag); // send process id and results
	recv(&row, Pmaster , source_tag);
}

      上述程式其實是用來計算曼德勃羅數集的一部分,首先master連續的給所有processer分配任務,誰先做完任務則回來取下一個任務,這樣就實現了基本的動態平衡。

 3.2.2  Decentralized Work Pool (分散的工作池)

 

       這個動態分配演算法,是在 Centralized Work Pool(工作池)上進行的改進,前者完全依賴master來分配任務,這裡在每個processer中可以再進行一次任務的分配。

 3.3.3  Fully Distributed Work Pool (完全分散式的工作池)

   特點: 每個process擁有或生成自己的任務,而且process之間互相通訊可以互相傳遞任務(偷任務),很明顯這種結構程式邏輯編寫難度較大、而且processer之間的通訊壓力也很大,但是任務分配的更加平均。

4、易並行程式舉例

   CS542200 Parallel Programming 中的 Homework 2:要求計算Mandelbrot Set(曼德勃羅數集),等啥時候寫完啥時候上傳吧。先貼一張Mandelbrot Set圖。曼德勃羅數集一般用於影像分形學,而且每個畫素點的迭代時間無法確定(屬於易並行程式設計問題,但需要動態分配任務)。

 

-----------------------------------------------------------------------------

上述圖片摘自《CS542200 Parallel Programming 》、《並行程式設計》

該文章為原創,轉載請註明出處。

-----------------------------------------------------------------------------

 

 

 

   

   

 

   

相關文章