簡體   English   中英

為什么此代碼的並行執行比順序執行慢?

[英]Why is parallel execution slower than sequential for this code?

#pragma omp parallel
{
 for (i=1; i<1024; i++)
  #pragma omp for
  for (j=1; j<1024; j++)
   A[i][j] = 2*A[i-1][j];
}

我正在使用12個線程來執行此代碼。 有什么建議我必須加快速度嗎?

假設A的類型小於64Byte,嘗試以這種方式並行化內部循環很可能會導致您在緩存行中共享錯誤。

假設A是4字節整數的對齊數組,則在同一緩存行中將具有A [i] [0]至A [i] [15]。 這意味着所有12個線程都將嘗試同時讀取該行,每個線程都需要讀取該行,如果您將其留在那,則可能導致該行在多個內核之間共享,但是您還嘗試寫回去,導致每個核心嘗試擁有所有權以進行修改。

CPU緩存通常基於基於MESI的協議,使存儲嘗試發出所有權的讀取,這將使除請求者之外的每個其他內核中的行無效。 發出12個並行信號(如果您有6個核心* 2個線程,則發出6個並行信號)將導致比賽,其中第一個贏得該線路的人很可能在沒有機會對其進行修改之前就搶先一步搶走了他(雖然不太可能)。 結果是非常混亂的,並且可能需要一段時間才能將生產線依次轉到每個核心,然后進行修改,然后被另一個核心偵聽。 下一個連續的16個元素組中的每個重復出現一次(同樣,假定為int)。

您可能會做的是:

  • 確保每個單獨的線程都在其自己的高速緩存行上工作,但是要添加一個內部循環,該循環運行每行所需數量的元素,並並行化跳過該數量元素的循環。

但是,這將使您無法充分利用CPU的潛力,因為您會丟失代碼的空間局部性和流屬性。 相反,您可以:

  • 並行化外循環,以使每個線程可以占用幾行,從而使其擁有整個連續的內存流。 但是,由於需要在行之間進行排序,因此您可能需要在此處進行一些調整(例如,轉置)。

這里仍然有一個缺點,因為如果線程遇到太多的流,可能會失去對它們的跟蹤。 因此,第三種方法是-

  • 平鋪該數組-將其分成48行,在線程之間分配它們,以便每個線程都在幾行上運行(轉置技巧仍然適用於此處,順便說一句),然后繼續進行下一組

1)您擁有幾個核心? 您無法獲得比這更多的並行化速度,而且正如其他人所說的那樣,可能要少得多。

2)看起來內部索引j應該從0開始,而不是1。

3)內循環正在為指針和展開吶喊,就像

double* pa = &A[i][0];
double* pa1 = &A[i-1][0];
for (j = 0; j < 1024; j += 8){
    *pa++ = 2 * *pa1++;
    *pa++ = 2 * *pa1++;
    *pa++ = 2 * *pa1++;
    *pa++ = 2 * *pa1++;
    *pa++ = 2 * *pa1++;
    *pa++ = 2 * *pa1++;
    *pa++ = 2 * *pa1++;
    *pa++ = 2 * *pa1++;
}

要么...

double* pa = &A[i][0];
double* paEnd = &A[i][1024];
double* pa1 = &A[i-1][0];
for (; pa < paEnd; pa += 8, pa1 += 8){
    pa[0] = 2 * pa1[0];
    pa[1] = 2 * pa1[1];
    pa[2] = 2 * pa1[2];
    pa[3] = 2 * pa1[3];
    pa[4] = 2 * pa1[4];
    pa[5] = 2 * pa1[5];
    pa[6] = 2 * pa1[6];
    pa[7] = 2 * pa1[7];
}

以較快者為准。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM