【问题标题】:values stored in array changed in OpenMP gcc存储在数组中的值在 OpenMP gcc 中更改
【发布时间】:2018-07-18 17:19:14
【问题描述】:

我最近开始使用 gcc 探索 OpenMP。 基本上我正在执行这段代码来演示 Eratosthenes 的筛子。

void main() {
    int n, iterate_limit;

    printf("Enter the number limit for sieve of erathoros:");
    scanf("%d", &n);
    iterate_limit = (int) sqrt((double) n);
    printf("iterate limit is %d\n", iterate_limit);
    int primes[n], prime_index = 0;
    int non_primes[n], non_prime_index = 0;

    #pragma omp parallel for
    for (int i = 2; i <= iterate_limit; i++) {
        if (isprime(i) == 1) {
            int num = i;

            printf("prime is %d\n", i);
            primes[prime_index] = num;
            prime_index++;
            num += i;
            while (num < n) {
                non_primes[non_prime_index] = num;
                non_prime_index++;
                num += i;
            }
        }
    }

    printf("primes are\n"); 
    for (int i = 0; i < prime_index; i++) {
        printf("%d\n ", primes[i]);  
    }
}

理想情况下,数组primes 应该包含所有要消除其倍数的数字,它适用于输入 25 或 50。但奇怪的是,当输入更大的数字(例如 99 或 125)时, primes 数组中的值与预期不同。 即使printf("prime is %d\n",i); 给出了有效的输出。 这是 125 作为输入的输出:

Enter the number limit for sieve of erathoros:125
iterate limit is 11
[New LWP 17676]
[New LWP 17677]
[New LWP 17678]
prime is 11
prime is 5
prime is 7
prime is 2
prime is 3
primes are
60
 63
 66
 69
 72
 [LWP 17676 exited]
[LWP 17677 exited]
[LWP 17674 exited]
[Inferior 1 (process 17674) exited normally]

为什么我得到 60,63,66,69,72 而不是 2,3,5,7,11?

我正在运行 Ubuntu 18。

编辑:

正如Osiris 所指出的,我没有检查我的non_primes 数组中的重复项,这似乎覆盖了我的primes 数组。重复消除确实提供了一个临时解决方案。 但是由于John Bollinger 提到的数据竞争,增加输入数或增加线程数会导致不正确的结果@

【问题讨论】:

    标签: c gcc parallel-processing openmp


    【解决方案1】:

    你有两个问题,一个是战术的,一个是战略的。

    战术问题是 OpenMP 并不能减轻您对数据竞争的关注。 OpenMP 线程共享变量primesprime_index 等。你依靠它。但是你所有的线程都读写prime_index,这不是原子的,甚至不是volatile,没有任何线程间同步。这会产生数据竞争,因此您的程序的行为是未定义的。从语言的角度来看,这就是故事的结局。 “未定义”的意思是它所说的。

    在实践中,数据竞争的常见表现形式包括不同的线程看不到彼此对相关共享变量的写入,或者看不到它们以令人惊讶的顺序。类似的事情似乎就是这里正在发生的事情。

    战略问题是埃拉托森斯筛法是并行化的糟糕选择。或者,至少它的外循环是。这是因为(外部)循环迭代之间的数据依赖性。为了使筛子正常工作,需要按顺序筛出每个素数的倍数,因为相关的素数测试可以归结为“候选 X 是否被筛出为少一点?”如果外部循环的迭代并行而不是串行运行,您将无法可靠地回答这个问题。


    这两个问题是分开且独立的,因为解决数据竞争问题并不能解决数据依赖问题,并且可以通过大量工作编写一个并行的 Eratosthenes 筛子来正确观察数据依赖项(我实际上已经做到了,尽管是在 Java 中而不是 C 中),并且由于数据竞争,这种方法仍然可能被破坏。

    顺便说一句,即使是正确有效的并行 Sieve 也会受到数据依赖性的影响,因为满足它们会将其限制为具有相当差的加速因子。

    【讨论】:

    • 原来问题是primesnon_primes 的超出范围的值覆盖,出于某种原因,即使对于单线程执行,我也没有超出范围异常。
    • @AnkurRanjan,C 不承诺超出范围的数组访问例外。在任何情况下,解决越界写入问题似乎可以为您提供预期的程序行为,但这并不意味着可以忽略数据竞争和依赖问题。作为学习并行编程的人,您应该特别对理解这些问题感兴趣。
    • 正式指出,增加线程数并提供更大的输入确实会导致数据竞争和依赖性问题,给出负数和不正确的数字。感谢您提及:)
    【解决方案2】:

    如果您删除 OpenMP,您仍然会得到错误的输出,因为您的写入超出了 non_primes 数组的范围。

    正确的是,小于n 的非素数不能超过n,但你写的一些值不止一次。

    例如,在i=2 中,您在non_primes 数组中写入除2 之外的所有偶数。 在i=3,您在其中写入 6,9,12,15,18,..,其中偶数现在是数组元素的两倍。您需要检查数字是否已经是数组的元素。

    【讨论】:

    • 是的,我删除了 OpenMP 并遇到了同样的问题,事实证明,当我为non_primes 写越界时,它会覆盖到primes。因此,添加一个函数来检查 non_primes 中是否已存在该值可以解决单线程和多线程解决方案的问题。
    • @AnkurRanjan 是的,但要使其在多线程中可靠工作,您必须确保变量的数据访问是原子的。另请参阅其他答案。
    猜你喜欢
    • 2021-04-09
    • 1970-01-01
    • 2023-03-17
    • 1970-01-01
    • 2010-09-21
    • 1970-01-01
    • 1970-01-01
    • 2021-01-16
    相关资源
    最近更新 更多