跳轉到內容

OpenMP/歸約

來自華夏公益教科書,自由的教科書

求和浮點數

[編輯 | 編輯原始碼]

對於我們的第一個並行程式,我們轉向一個古老的問題:求和一個浮點數陣列。解決這個問題的基本演算法非常簡單,它允許我們專注於 OpenMP 特性而不是演算法細節,但我們很快就會發現,這個問題實際上並不像一開始看起來那麼簡單。

不用多說,以下是一個順序演算法來求和浮點數列表

#include <stddef.h>  // for size_t

float sum(const float *a, size_t n)
{
    float total;
    size_t i;

    for (i = 0, total = 0.; i < n; i++) {
        total += a[i];
    }
    return total;
}

就演算法而言,這個演算法很簡單。將上面的定義放入檔案 isum.c(迭代求和)中。

如果您有處理浮點數的經驗,您可能會想將 total 設定為 double 而不是 float 以獲得更高的精度。現在不要這樣做,因為我們稍後會用另一種方法來解決精度問題。

為了測試我們的演算法,我們需要一個驅動程式,我們將把它放在檔案 main.c

#include <stdio.h>
#include <stdlib.h>

float sum(const float *, size_t);

#define N 1000000  // we'll sum this many numbers

int main()
{
    float *a = malloc(N * sizeof(float));
    if (a == NULL) {
        perror("malloc");
        return 1;
    }

    // fill the array a
    for (size_t i = 0; i < N; i++) {
        a[i] = .000001;
    }

    printf("%f\n", sum(a, N));
    return 0;
}

最後,我們需要一些方法來構建這個程式。在 Linux/Unix/OS X 上,以下 Makefile 應該可以完成這項工作。它假設您使用的是 GCC。

# C99 extensions are not necessary for OpenMP, but very convenient
CFLAGS = -fopenmp -Wall -std=c99
LDFLAGS = -fopenmp

OBJS = main.o isum.o

# when copy-pasting the following, be aware that the indent must be a tab, not spaces
sum: $(OBJS)
        $(CC) $(LDFLAGS) -o sum $(OBJS)

現在用 make sum 編譯程式,執行它,並使用 time 等工具檢視它的速度。如果速度太快無法測量,請考慮將 N 更改為更大的數字,或者在迴圈中執行 sum,而不僅僅執行一次。

並行求和的方式

[編輯 | 編輯原始碼]

我們不得不經過一些設定,但現在我們已經準備好為浮點數建立一個並行求和演算法。它在這裡

#include <stddef.h>  // for size_t

float sum(const float *a, size_t n)
{
    float total = 0.;

    #pragma omp parallel for reduction(+:total)
    for (size_t i = 0; i < n; i++) {
        total += a[i];
    }
    return total;
}

#pragma omp parallel for 將迴圈變成一個並行迴圈。如果您有兩個核心,OpenMP 將(可能)使用兩個執行緒,每個執行緒執行一半的迴圈。reduction(+:total) 宣告我們正在對輸入陣列進行歸約,透過求和到變數 total,所以部分迴圈完成後,他們的結果必須被加到這個變數中。

將此程式碼放入 isum.c 中並重新編譯。現在執行程式。您得到的結果是否與之前相同?

練習:使用環境變數 OMP_NUM_THREADS 的各種設定執行程式,該變數控制 OpenMP 使用的執行緒池的大小。嘗試 1、2、4 和 8。您是否看到每個設定的相同結果?現在嘗試荒謬的大量執行緒,例如 16000。這如何影響效能?
練習:兩個向量的點積是它們各自項的乘積的總和,. 修改 sum 函式以用於平行計算點積的 dot 函式。
華夏公益教科書