我在 C 上使用 OpenMP 时遇到问题
I am having trouble with OpenMP on C
我想并行化 for 循环,但我似乎无法理解这个概念,每次我尝试并行化它们时,它仍然有效,但速度显着降低。
for(i=0; i<nbodies; ++i){
for(j=i+1; j<nbodies; ++j) {
d2 = 0.0;
for(k=0; k<3; ++k) {
rij[k] = pos[i][k] - pos[j][k];
d2 += rij[k]*rij[k];
if (d2 <= cut2) {
d = sqrt(d2);
d3 = d*d2;
for(k=0; k<3; ++k) {
double f = -rij[k]/d3;
forces[i][k] += f;
forces[j][k] -= f;
}
ene += -1.0/d;
}
}
}
}
我尝试在某些情况下使用 barrier 和 critical 同步,但没有任何反应,或者处理根本没有结束。
更新,这是我现在的状态。工作没有崩溃,但我添加的线程越多,计算时间就越糟糕。 (锐龙 5 2600 6/12)
#pragma omp parallel shared(d,d2,d3,nbodies,rij,pos,cut2,forces) private(i,j,k) num_threads(n)
{
clock_t begin = clock();
#pragma omp for schedule(auto)
for(i=0; i<nbodies; ++i){
for(j=i+1; j<nbodies; ++j) {
d2 = 0.0;
for(k=0; k<3; ++k) {
rij[k] = pos[i][k] - pos[j][k];
d2 += rij[k]*rij[k];
}
if (d2 <= cut2) {
d = sqrt(d2);
d3 = d*d2;
#pragma omp parallel for shared(d3) private(k) schedule(auto) num_threads(n)
for(k=0; k<3; ++k) {
double f = -rij[k]/d3;
#pragma omp atomic
forces[i][k] += f;
#pragma omp atomic
forces[j][k] -= f;
}
ene += -1.0/d;
}
}
}
clock_t end = clock();
double time_spent = (double)(end - begin) / CLOCKS_PER_SEC;
#pragma omp single
printf("Calculation time %lf sec\n",time_spent);
}
我将计时器合并到实际的并行代码中(我认为这样可以快几毫秒)。此外,我认为我正确地掌握了大部分共享变量和私有变量。在文件中输出力。
如果未同步的工作量不是很大的话,使用障碍或其他同步会降低您的代码速度。你不是这样的。您可能需要重新制定代码以删除同步。
你正在做类似 N 体模拟的事情。我在这里制定了几个解决方案:https://pages.tacc.utexas.edu/~eijkhout/pcse/html/omp-examples.html#N-bodyproblems
此外:您的 d2
循环是一个缩减,因此您可以这样对待它,但如果该变量对 i,j
迭代是私有的,这可能就足够了。
您应该始终在所需的最小范围内定义变量,尤其是在性能有问题的情况下。 (请注意,如果您这样做,您的编译器可以创建更高效的代码)。除了性能,它还有助于避免数据竞争。
我认为您放错了大括号,第一个 for
循环中的条件应该是 i<nbodies-1
。变量 ene
可以使用 reduction 来总结,为了避免数据竞争原子操作必须用于增加数组 forces
,所以你不需要使用慢屏障或临界区。您的代码应如下所示(假设 int
用于索引,double
用于计算):
#pragma omp parallel for reduction(+:ene)
for(int i=0; i<nbodies-1; ++i){
for(int j=i+1; j<nbodies; ++j) {
double d2 = 0.0;
double rij[3];
for(int k=0; k<3; ++k) {
rij[k] = pos[i][k] - pos[j][k];
d2 += rij[k]*rij[k];
}
if (d2 <= cut2) {
double d = sqrt(d2);
double d3 = d*d2;
for(int k=0; k<3; ++k) {
double f = -rij[k]/d3;
#pragma omp atomic
forces[i][k] += f;
#pragma omp atomic
forces[j][k] -= f;
}
ene += -1.0/d;
}
}
}
}
已解决,原来我需要的只是
#pragma omp parallel for nowait
也不需要“原子”。
奇怪的解决方案,我不完全理解它是如何工作的,但它也确实输出文件有 0 个损坏的结果。
我想并行化 for 循环,但我似乎无法理解这个概念,每次我尝试并行化它们时,它仍然有效,但速度显着降低。
for(i=0; i<nbodies; ++i){
for(j=i+1; j<nbodies; ++j) {
d2 = 0.0;
for(k=0; k<3; ++k) {
rij[k] = pos[i][k] - pos[j][k];
d2 += rij[k]*rij[k];
if (d2 <= cut2) {
d = sqrt(d2);
d3 = d*d2;
for(k=0; k<3; ++k) {
double f = -rij[k]/d3;
forces[i][k] += f;
forces[j][k] -= f;
}
ene += -1.0/d;
}
}
}
}
我尝试在某些情况下使用 barrier 和 critical 同步,但没有任何反应,或者处理根本没有结束。
更新,这是我现在的状态。工作没有崩溃,但我添加的线程越多,计算时间就越糟糕。 (锐龙 5 2600 6/12)
#pragma omp parallel shared(d,d2,d3,nbodies,rij,pos,cut2,forces) private(i,j,k) num_threads(n)
{
clock_t begin = clock();
#pragma omp for schedule(auto)
for(i=0; i<nbodies; ++i){
for(j=i+1; j<nbodies; ++j) {
d2 = 0.0;
for(k=0; k<3; ++k) {
rij[k] = pos[i][k] - pos[j][k];
d2 += rij[k]*rij[k];
}
if (d2 <= cut2) {
d = sqrt(d2);
d3 = d*d2;
#pragma omp parallel for shared(d3) private(k) schedule(auto) num_threads(n)
for(k=0; k<3; ++k) {
double f = -rij[k]/d3;
#pragma omp atomic
forces[i][k] += f;
#pragma omp atomic
forces[j][k] -= f;
}
ene += -1.0/d;
}
}
}
clock_t end = clock();
double time_spent = (double)(end - begin) / CLOCKS_PER_SEC;
#pragma omp single
printf("Calculation time %lf sec\n",time_spent);
}
我将计时器合并到实际的并行代码中(我认为这样可以快几毫秒)。此外,我认为我正确地掌握了大部分共享变量和私有变量。在文件中输出力。
如果未同步的工作量不是很大的话,使用障碍或其他同步会降低您的代码速度。你不是这样的。您可能需要重新制定代码以删除同步。
你正在做类似 N 体模拟的事情。我在这里制定了几个解决方案:https://pages.tacc.utexas.edu/~eijkhout/pcse/html/omp-examples.html#N-bodyproblems
此外:您的 d2
循环是一个缩减,因此您可以这样对待它,但如果该变量对 i,j
迭代是私有的,这可能就足够了。
您应该始终在所需的最小范围内定义变量,尤其是在性能有问题的情况下。 (请注意,如果您这样做,您的编译器可以创建更高效的代码)。除了性能,它还有助于避免数据竞争。
我认为您放错了大括号,第一个 for
循环中的条件应该是 i<nbodies-1
。变量 ene
可以使用 reduction 来总结,为了避免数据竞争原子操作必须用于增加数组 forces
,所以你不需要使用慢屏障或临界区。您的代码应如下所示(假设 int
用于索引,double
用于计算):
#pragma omp parallel for reduction(+:ene)
for(int i=0; i<nbodies-1; ++i){
for(int j=i+1; j<nbodies; ++j) {
double d2 = 0.0;
double rij[3];
for(int k=0; k<3; ++k) {
rij[k] = pos[i][k] - pos[j][k];
d2 += rij[k]*rij[k];
}
if (d2 <= cut2) {
double d = sqrt(d2);
double d3 = d*d2;
for(int k=0; k<3; ++k) {
double f = -rij[k]/d3;
#pragma omp atomic
forces[i][k] += f;
#pragma omp atomic
forces[j][k] -= f;
}
ene += -1.0/d;
}
}
}
}
已解决,原来我需要的只是
#pragma omp parallel for nowait
也不需要“原子”。
奇怪的解决方案,我不完全理解它是如何工作的,但它也确实输出文件有 0 个损坏的结果。