[並行計算]Matlab並行計算工具箱(Parallel Computing Toolbox)官方文檔教程中文版(4)

嵌套的parfor循環和for循環

如下面的代碼所示:

Code #4-1
1.parfor i = 1:10  
2.MyFun(i)  
3.end  
4.
5.function MyFun(i)  
6.parfor j = 1:5  
7.  	...  
8.end  
9.end  

代碼#4-1爲在循環裏面調用一個函數,同時該函數也使用parfor做並行計算,然而,在語句parfor i = 1:10執行後,處理器的並行計算就只會落在這條語句,其餘後面開啓的並行parfor並不會生效。

 

將嵌套循環轉換爲parfor時的注意點

下列代碼片實現一個典型的雙循環二維數組賦值功能,在我的電腦上跑出來的時間約爲42.3秒。

Code #4-2
1.A = 100;  
2.tic  
3.for i = 1:100  
4.for j = 1:100  
5.a(i,j) = max(abs(eig(rand(A))));  
6.end  
7.end  
8.toc  

現在,由我們前文的敘述可知,雙圈循環只能改裝一個for爲parfor實現並行計算,那麼,在這一個代碼片#4-2中應該改裝哪個for才能實現最高效的計算呢,下面給出一種度量方法,在代碼片#4-3中加入了輸入輸出數據量觀察ticBytes字眼,並改裝第一個for循環爲parfor循環。

Code #4-3
1.A = 100;  
2.tic  
3.ticBytes(gcp);
4.parfor i = 1:100  
5.for j = 1:100  
6.a(i,j) = max(abs(eig(rand(A))));  
7.end  
8.end 
9.tocBytes(gcp)
toc 

在筆者的雙核筆記本上跑出來差不多是快了2倍,證明這個parfor的改裝是具備較高的效益的,下面我們來看改裝另一個parfor的結果代碼#4-4。

Code #4-4
1.A = 100;  
2.tic  
3.ticBytes(gcp);
4.for i = 1:100  
5.parfor j = 1:100  
6.a(i,j) = max(abs(eig(rand(A))));  
7.end  
8.end 
9.tocBytes(gcp)
toc 

跑出來的時間稍微慢了一點,雖然不算很差,認真看我們發現內嵌的parfor循環的數據量比外循環版本大了一個量級,那麼爲什麼會出現這樣的差別呢,萬能的Matlab官方文檔中並沒有給出答案,本文作者給出的一個合理的解釋如下:

  1. 使用外層的並行計算,內層做多而數據量不大的循環計算,cpu的物理內核從一開始就分配到了內層的一個整個循環任務,因此計算的效率較高,傳入傳出的數據量較少;
  2. 相比較下,使用內層作爲parfor並行的話,在任務上的計算是和外層一致的,但一圈j = 1:100計算完後,parfor結束,由外層循環i重新進入parfor循環時,cpu會需要消耗進入parfor模式分配並行,此時是存在開銷的;

這個實驗證明了頻繁啓動parfor是有負面影響的,因此改裝外層並行比內層快是合理的。

嵌套循環中變量的使用注意事項

觀察下面左邊的代碼片在內層循環中j的上限需要實時計算矩陣A的列數,然而,在數據的處理段同樣有讀寫數組A的行爲,因此這樣的效率是不高的,如右側代碼片所示,先把矩陣A的列上限求出再進行計算是一個高效的方法。

無效

有效

A = zeros(100, 200);

parfor i = 1:size(A, 1)

   for j = 1:size(A, 2)

      A(i, j) = i + j;

   end

end

A = zeros(100, 200);

n = size(A, 2);

parfor i = 1:size(A,1)

   for j = 1:n

      A(i, j) = i + j;

   end

end

如下面左面代碼片的問題是使用隱含的矩陣索引j+1,在並行計算中索引數組時應儘量使用循環i和j的直接索引寫矩陣,右代碼片所示。

無效

有效

A = zeros(4, 11);

parfor i = 1:4

   for j = 1:10

      A(i, j + 1) = i + j;

   end

end

A = zeros(4, 11);

parfor i = 1:4

   for j = 2:11

      A(i, j) = i + j - 1;

   end

end

 

如下面的代碼片所示,若嵌套循環中使用了矩陣A,則不可以在第一層循環中直接讀取矩陣A是,因爲第一層循環正在並行計算的模式下,右邊的代碼是合法的,因爲其聲明瞭一個臨時變量

 

無效

有效

A = zeros(4, 10);

parfor i = 1:4

    for j = 1:10

        A(i, j) = i + j;

    end

    disp(A(i, 1))

end

A = zeros(4, 10);

parfor i = 1:4

    v = zeros(1, 10);

    for j = 1:10

        v(j) = i + j;

    end

    disp(v(1))

    A(i, :) = v;

end

 

下面左邊代碼的問題是,在parfor循環的內嵌循環中,索引不允許割裂,右則代碼是一個正確的示範,可以允許通過條件分支判斷,但循環索引是需要連貫的。

 

無效

有效

A = zeros(4, 10);

parfor i = 1:4

   for j = 1:5

      A(i, j) = i + j;

   end

   for k = 6:10

      A(i, k) = pi;

   end

end

A = zeros(4, 10);

parfor i = 1:4

   for j = 1:10

      if j < 6

         A(i, j) = i + j;

      else

         A(i, j) = pi;

      end

   end

end

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章