跳转到内容

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 函数。
华夏公益教科书