【问题标题】:Harmonic progression sum c++ openMP谐波级数和 c++ openMP
【发布时间】:2016-06-07 22:51:40
【问题描述】:

我正在尝试使用 opemMP 制作并行版本的“Harmonic Progression Sum”问题。 但是输出因输入而异。 (并行和顺序)

计划:

#include "stdafx.h"
#include <iostream>
#include <sstream>
#include <omp.h>
#include <time.h>

#define d 10    //Numbers of Digits (Example: 5 => 0,xxxxx)
#define n 1000  //Value of N (Example: 5 => 1/1 + 1/2 + 1/3 + 1/4 + 1/5)

using namespace std;

void HPSSeguencial(char* output) {
    long unsigned int digits[d + 11];

    for (int digit = 0; digit < d + 11; ++digit)
        digits[digit] = 0;

    for (int i = 1; i <= n; ++i) {
        long unsigned int remainder = 1;
        for (long unsigned int digit = 0; digit < d + 11 && remainder; ++digit) {
            long unsigned int div = remainder / i;
            long unsigned int mod = remainder % i;
            digits[digit] += div;
            remainder = mod * 10;
        }
    }


    for (int i = d + 11 - 1; i > 0; --i) {
        digits[i - 1] += digits[i] / 10;
        digits[i] %= 10;
    }
    if (digits[d + 1] >= 5) {
        ++digits[d];
    }


    for (int i = d; i > 0; --i) {
        digits[i - 1] += digits[i] / 10;
        digits[i] %= 10;
    }
    stringstream stringstreamA;
    stringstreamA << digits[0] << ",";


    for (int i = 1; i <= d; ++i) {
        stringstreamA << digits[i];
    }
    string stringA = stringstreamA.str();
    stringA.copy(output, stringA.size());
}

void HPSParallel(char* output) {
    long unsigned int digits[d + 11];

    for (int digit = 0; digit < d + 11; ++digit)
        digits[digit] = 0;

    int i;
    long unsigned int digit;
    long unsigned int remainder;
    #pragma omp parallel for private(i, remainder, digit)
    for (i = 1; i <= n; ++i) {
        remainder = 1; 
        for (digit = 0; digit < d + 11 && remainder; ++digit) {
            long unsigned int div = remainder / i;
            long unsigned int mod = remainder % i;
            digits[digit] += div;
            remainder = mod * 10;
        }
    }

    for (int i = d + 11 - 1; i > 0; --i) {
        digits[i - 1] += digits[i] / 10;
        digits[i] %= 10;
    }
    if (digits[d + 1] >= 5) {
        ++digits[d];
    }

    for (int i = d; i > 0; --i) {
        digits[i - 1] += digits[i] / 10;
        digits[i] %= 10;
    }
    stringstream stringstreamA;
    stringstreamA << digits[0] << ",";

    for (int i = 1; i <= d; ++i) {
        stringstreamA << digits[i];
    }
    string stringA = stringstreamA.str();
    stringA.copy(output, stringA.size());
}

int main() {
    //Sequential Method
    cout << "Sequential Method: " << endl;
    char outputSeguencial[d + 10];
    HPSSeguencial(outputSeguencial);
    cout << outputSeguencial << endl;

    //Cleaning vector
    string stringA = "";
    stringA.copy(outputSeguencial, stringA.size());

    //Parallel Method
    cout << "Parallel Method: " << endl;
    char outputParallel[d + 10];
    HPSParallel(outputParallel);
    cout << outputParallel << endl;

    system("PAUSE");
    return 0;
}

示例:

输入:

#define d 10
#define n 1000

输出:

Sequential Method:
7,4854708606╠╠╠╠╠╠╠╠╠╠╠╠
Parallel Method:
6,6631705861╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠ÇJ^

输入:

#define d 12
#define n 7

输出:

Sequential Method:
2,592857142857╠╠╠╠╠╠╠╠╠╠╠╠╠╠ÀÂ♂ü─¨@
Parallel Method:
2,592857142857╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠ÇJJ

问候

粘贴代码

http://pastecode.org/index.php/view/62768285

【问题讨论】:

    标签: c++ parallel-processing openmp


    【解决方案1】:

    更新digits 数组时,您的线程会互相踩踏。因此会丢失一些添加内容,并且您会得到虚假结果(几乎可以肯定,不同运行中的不同结果)。

    您必须将写入同步到digits,例如带有原子(或关键)部分:

    // ... <snip>
    #pragma omp parallel for private(i, remainder, digit)
    for (i = 1; i <= n; ++i) {
        remainder = 1; 
        for (digit = 0; digit < d + 11 && remainder; ++digit) {
            long unsigned int div = remainder / i;
            long unsigned int mod = remainder % i;
            #pragma omp atomic     // <- HERE, could also be #pragma omp critical
            digits[digit] += div;
            remainder = mod * 10;
        }
    }
    // <snip> ...
    

    这样一次只有一个线程可以更新数组。不过,对于这样的任务,这可能会抵消将任务拆分为多个线程的任何好处。

    【讨论】:

    • 一些编译器(读取 GCC)在 x86 上实现原子 +=lock addl。关键部分是用互斥体实现的,包括运行时函数调用,因此比原子慢得多。实际上,由于内部循环是有规律的,线程会在多次迭代后延迟同步,并且不会有等待。缓存一致性会损害并行增益。
    【解决方案2】:

    正如Daniel Fischer 指出的那样,你有一个写冲突,但你可以比omp critical 部分更优雅地避免它,例如通过给每个线程它自己的digits 副本并在循环结束时聚合它们。

    【讨论】:

    • 更优雅的解决方案是在 Fortran 中重写代码,因为 OpenMP 支持减少 Fortran 中的数组变量:)
    猜你喜欢
    • 2012-11-12
    • 2012-11-10
    • 2023-03-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-01
    • 2015-08-06
    • 2022-01-13
    相关资源
    最近更新 更多