【问题标题】:one strange problem when I generate random numbers by OMP(C++)当我通过 OMP(C++)生成随机数时出现一个奇怪的问题
【发布时间】:2021-11-15 04:12:13
【问题描述】:

代码如下:

#include<omp.h>
#include<stdio.h>
#include<stdlib.h>

int main()
{
    unsigned int seed = 1;
    int n =4;
    int i = 0;
#pragma omp parallel for num_threads(4) private(seed)
    for(i=0;i<n;i++)
    {
        int temp1 = rand_r(&seed);
        printf("\nRandom number: %d by thread %d\n", temp1, omp_get_thread_num());
    }
    return 0;
}

代码输出为:

Random number: 1905891579 by thread 0

Random number: 1012484 by thread 1

Random number: 1012484 by thread 2

Random number: 1012484 by thread 3

这对我来说很奇怪:为什么线程 0 有不同的编号? 但是当我将 n 更改为 const 数字 4 时:

#include<omp.h>
#include<stdio.h>
#include<stdlib.h>

int main()
{
    unsigned int seed = 1;
    const int n =4;
    int i = 0;
#pragma omp parallel for num_threads(4) private(seed)
    for(i=0;i<n;i++)
    {
        int temp1 = rand_r(&seed);
        printf("\nRandom number: %d by thread %d\n", temp1, omp_get_thread_num());
    }
    return 0;
}

代码输出为:

Random number: 1012484 by thread 2

Random number: 1012484 by thread 3

Random number: 1012484 by thread 0

Random number: 1012484 by thread 1

所有线程都有相同的随机数。 我不明白当 n 不是 const 变量时线程 0 具有不同数字的原因。 有没有人知道这件事?非常感谢。

【问题讨论】:

  • 你确定这是一个“c++”问题吗? #include 的包含似乎表明您正在编写“C”代码。随机数是因为两次运行都使用相同的“种子”。对于 C++,请在此处查找随机数生成:en.cppreference.com/w/cpp/numeric/random/random_device。在 c++ 中也尽量避免使用 printf(这是不安全的:stackoverflow.com/questions/64042652/…)。
  • @PepijnKramer “随机数是 [...],因为两次运行都使用相同的“种子”。”是什么?完全相同的?根据 OP 的输出报价,它们不是。
  • @Yunnosch 在第一个版本的代码中,我使用相同的种子,但线程 0 得到不同的随机数。这很奇怪。
  • @PepijnKramer 在第一个版本的代码中,我使用相同的种子,但是线程 0 的随机数不同。这很奇怪
  • 我知道。 Pepijn 似乎没有。

标签: c++


【解决方案1】:

seed 被声明为private,所以在循环中它没有初始化值,更多的是,每​​个实例可能有也可能没有不同的值,使用它的值是 UB。您可以使用firstprivate 让私有实例初始化为原始值。

#pragma omp parallel for num_threads(4) firstprivate(seed)

另外,rand_r 不是标准函数,而是 2 级线程安全的 POSIX 函数。级别 2 意味着 rand_r 仅在用于单独的对象/数据源时才是安全的,这里不是这种情况,我将使用相同的种子计数器。在这种情况下,被调用的线程的顺序是不确定的和不同步的。奇怪的输出是可能的,因为任何一个线程都可能先于其他线程更新种子的值。

【讨论】:

  • 那我应该使用什么而不是rand_r?你能告诉我正确的代码吗?非常感谢。
  • @majestic 您必须构建线程安全生成器(这将导致后续执行)或为每个线程使用单独的生成器,但在这种情况下,将它们的起始值设置为非预定的问题方式。
  • seed 被声明为 private 因此根据 omp doc,每个线程都应该有自己的原始种子副本。这里不应该有任何线程安全问题,只有随机播种问题。目前尚不清楚,在这种情况下,线程 0 如何在第一种情况下生成不同的输出
  • @GuillaumeGris hm,我认为这样的使用是不正确的。如果每个线程的seedprivate,在这种情况下seed 没有被初始化.. 对const int n 的反应是UB 的侥幸和结果,在不同的系统行为上完全相反。
  • @majestic:如果您确实在使用 C++,请查看&lt;random&gt;。 C++ 随机数生成器是对象,因此您可以拥有多个相同的 RNG。
【解决方案2】:

问题是声明为私有的变量没有初始化。 如果你添加这一行

  printf("thread %d seed %u \n", omp_get_thread_num(), seed);

int temp1 = rand_r(&amp;seed) 之前你会得到这样的输出:

thread 3 seed 0 

Random number: 1012484 by thread 3 seed 2802067423

thread 0 seed 21850 

Random number: 2082025184 by thread 0 seed 2464192641

thread 2 seed 0 

Random number: 1012484 by thread 2 seed 2802067423

thread 1 seed 0 

Random number: 1012484 by thread 1 seed 2802067423

重复运行,在我的机器上,我看到线程 0 总是得到不同的 seed 值,而其他线程有 seed=0。无论如何,这是由于未初始化的变量而导致的 UB。

【讨论】:

  • 非常感谢!原因如你所说!
猜你喜欢
  • 1970-01-01
  • 2020-08-04
  • 2019-06-04
  • 1970-01-01
  • 1970-01-01
  • 2021-05-16
  • 2017-04-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多