使用 OpenMP 分配数组的特殊指令?
Special directives for assigning array with OpenMP?
我在 OpenMP
中用于并行化的简单 for
循环是
vector< double > xs;
vector< double > ys;
xs.resize(N);
ys.resize(N);
if(rank0) printf("Assigning points ...\n");
#pragma omp parallel for
for(long i = 0; i < N; i++) {
xs[i] = ((double)rand()/(double)RAND_MAX);
ys[i] = ((double)rand()/(double)RAND_MAX);
}
但是当我包含 #pragma omp parallel for
时比不包含时花费的时间要长得多。当我没有使用正确的 reduction
或类似的东西时经常会看到这种情况,所以我想知道是否还需要为此 #pragma
.
做些什么
此 for
循环是否需要 #pragma
中的任何其他内容?
请注意,此问题与 rand()
的使用直接相关。
我的直接猜测是问题源于以下事实:rand()
使用每次调用 rand()
时都会更新的单个种子。这意味着即使您要写入的数组之间没有冲突,每次调用 rand()
都可能会强制线程之间进行同步。
有多种方法可以解决这个问题。一个明显的方法是使用 C++11 中提供的新随机数生成 类,每个线程都有一个单独的随机数生成器对象,如下所示:
std::mt19937_64 a;
std::mt19937_64 b;
std::uniform_real_distribution<double> da;
std::uniform_real_distribution<double> db;
#pragma omp parallel for private(a, b)
for (long i = 0; i < N; i++) {
xs[i] = da(a);
ys[i] = db(b);
}
至少在我的系统上进行的快速测试中,这个 运行s 单线程大约需要 4 秒,启用 OpenMP 大约需要 1 秒(这是在 4 核处理器上,所以这是接近完美缩放)。
请注意,如果您使用的是 32 位系统(或至少使用生成 32 位代码的编译器),那么使用 mt19937
而不是 [=15] 可能会快得多=].对于每个生成的数字,这将只有 32 位随机性,但这可能与 rand()
产生的一样多。在 64 位 system/compiler 上,期望 mt19937_64
到 运行 一样快,并产生更大的随机性。
另一个小注意事项:这里我刚刚为每个生成器使用了默认种子(即 1
)。您可能希望随机生成种子,例如从 std::random_device
生成种子,并分别为每个线程的生成器生成种子,这样您就不会在线程之间获得重复的数字。
原来rand
不是线程安全的。一个简单的替代方案是 drand48_r
我试图使用的。如下更改我的循环显示了我期望的确切加速:
#pragma omp parallel for private(ii, rBuf, trand) shared(xs,ys)
for(ii = 0; ii < N; ii++) {
drand48_r(&rBuf, &trand);
xs[ii] = trand;
drand48_r(&rBuf, &trand);
ys[ii] = trand;
}
我在 OpenMP
中用于并行化的简单 for
循环是
vector< double > xs;
vector< double > ys;
xs.resize(N);
ys.resize(N);
if(rank0) printf("Assigning points ...\n");
#pragma omp parallel for
for(long i = 0; i < N; i++) {
xs[i] = ((double)rand()/(double)RAND_MAX);
ys[i] = ((double)rand()/(double)RAND_MAX);
}
但是当我包含 #pragma omp parallel for
时比不包含时花费的时间要长得多。当我没有使用正确的 reduction
或类似的东西时经常会看到这种情况,所以我想知道是否还需要为此 #pragma
.
此 for
循环是否需要 #pragma
中的任何其他内容?
请注意,此问题与 rand()
的使用直接相关。
我的直接猜测是问题源于以下事实:rand()
使用每次调用 rand()
时都会更新的单个种子。这意味着即使您要写入的数组之间没有冲突,每次调用 rand()
都可能会强制线程之间进行同步。
有多种方法可以解决这个问题。一个明显的方法是使用 C++11 中提供的新随机数生成 类,每个线程都有一个单独的随机数生成器对象,如下所示:
std::mt19937_64 a;
std::mt19937_64 b;
std::uniform_real_distribution<double> da;
std::uniform_real_distribution<double> db;
#pragma omp parallel for private(a, b)
for (long i = 0; i < N; i++) {
xs[i] = da(a);
ys[i] = db(b);
}
至少在我的系统上进行的快速测试中,这个 运行s 单线程大约需要 4 秒,启用 OpenMP 大约需要 1 秒(这是在 4 核处理器上,所以这是接近完美缩放)。
请注意,如果您使用的是 32 位系统(或至少使用生成 32 位代码的编译器),那么使用 mt19937
而不是 [=15] 可能会快得多=].对于每个生成的数字,这将只有 32 位随机性,但这可能与 rand()
产生的一样多。在 64 位 system/compiler 上,期望 mt19937_64
到 运行 一样快,并产生更大的随机性。
另一个小注意事项:这里我刚刚为每个生成器使用了默认种子(即 1
)。您可能希望随机生成种子,例如从 std::random_device
生成种子,并分别为每个线程的生成器生成种子,这样您就不会在线程之间获得重复的数字。
原来rand
不是线程安全的。一个简单的替代方案是 drand48_r
我试图使用的。如下更改我的循环显示了我期望的确切加速:
#pragma omp parallel for private(ii, rBuf, trand) shared(xs,ys)
for(ii = 0; ii < N; ii++) {
drand48_r(&rBuf, &trand);
xs[ii] = trand;
drand48_r(&rBuf, &trand);
ys[ii] = trand;
}