代码之家  ›  专栏  ›  技术社区  ›  JMzance

蒙特卡罗积分的线程安全随机数生成

  •  3
  • JMzance  · 技术社区  · 14 年前

    我试图写一些非常快速计算随机数,可以应用于多线程的东西。我现在的代码是:

    /* Approximating PI using a Monte-Carlo method. */
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <math.h>
    #include <time.h>
    #include <omp.h>
    #define N 1000000000  /* As lareg as possible for increased accuracy */
    
    double random_function(void);
    
    int main(void)
    {
       int i = 0;
        double X, Y;
       double count_inside_temp = 0.0, count_inside = 0.0;
       unsigned int th_id = omp_get_thread_num();
       #pragma omp parallel private(i, X, Y) firstprivate(count_inside_temp)
       {
          srand(th_id);
          #pragma omp for schedule(static)
          for (i = 0; i <= N; i++) {
             X = 2.0 * random_function() - 1.0;
             Y = 2.0 * random_function() - 1.0;
             if ((X * X) + (Y * Y) < 1.0) {
            count_inside_temp += 1.0;
         }
      }
      #pragma omp atomic
          count_inside += count_inside_temp;
       }
       printf("Approximation to PI is = %.10lf\n", (count_inside * 4.0)/ N);
       return 0;
    }
    
    double random_function(void)
    {
       return ((double) rand() / (double) RAND_MAX);
    }
    

    1 回复  |  直到 10 年前
        1
  •  7
  •   Andrew Walker    14 年前

    rand() 线程安全?也许,也许不是:

    The rand() function need not be reentrant. A function that is not required to be reentrant is not required to be thread-safe."

    一个测试和良好的学习练习将取代对 兰德() 比如说,一个固定整数,看看会发生什么。

    1. 两个函数,一个用于设置初始种子(例如。 srand(seed) 兰德() ). PRNG的状态以全局变量的形式存储在内部。生成一个新的随机数不是线程安全的(很难说,但是输出流是不可复制的),就是在多线程代码中速度很慢(最终会在状态值周围进行一些序列化)。
    2. init_prng(seed) ,它返回一些PRNG状态的不透明表示, get_prng(state) destroy_peng(state) ,它只是清理分配的内存等等。使用这种类型的API的prng都应该是线程安全的,并且在没有锁定的情况下并行运行(因为您负责管理(现在是thread local)状态变量)。

    我一般用Fortran语言写,使用 Ladd's Mersenne Twister PRNG的实现(这个链接值得一读)。在C中有许多合适的PRNG,它们将状态暴露在您的控制之下。 PRNG

    最后,通常情况下,如果您一次性请求一个完整的随机数序列(例如,编译器可以对PRNG内部进行矢量化),则PRNG的性能会更好。因此,图书馆通常有 get_prng_array(state) get_prng 在一个循环中填充数组元素-他们只是做得更快。这将是第二次优化(需要在parallel for循环中添加for循环)。显然,您不希望这样做会耗尽每个线程的堆栈空间!