我试图写一些可以很快计算出随机数并且可以应用于多个线程的东西。我当前的代码是:
/* 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);
}
这可行,但是通过观察资源管理器,我知道它没有使用所有线程。 rand()是否适用于多线程代码?如果没有,还有一个很好的选择吗?非常感谢。 jack
最佳答案
rand()
线程安全吗?也许,也许不是:
一种测试和良好的学习习惯是,用一个固定的整数替换对rand()
的调用,然后看看会发生什么。
我认为伪随机数生成器的方式是一个黑匣子,它将一个整数作为输入并返回一个整数作为输出。对于任何给定的输入,输出始终是相同的,但是数字序列中没有模式,并且该序列在可能的输出范围内均匀分布。 (此模型并不完全准确,但是可以。)使用此黑盒的方式是选择一个凝视数字(种子),并使用应用程序中的输出值并将其作为下一次调用的输入随机数生成器。有两种设计API的常用方法:
srand(seed)
),一个用于从序列中检索下一个值(例如rand()
)。 PRNG的状态以某种全局变量的形式存储在内部。生成一个新的随机数要么不是线程安全的(很难说,但是输出流是不可再现的),要么是多线程代码中的速度很慢(最终会在状态值周围进行一些序列化)。 init_prng(seed)
(返回不透明的PRNG状态表示形式),get_prng(state)
(返回随机数并更改状态变量),以及destroy_peng(state)
(仅清除分配的内存等)。带有这种类型的API的PRNG应该都是线程安全的,并且在没有锁定的情况下并行运行(因为您负责管理(现在是线程本地)状态变量)。我通常使用Fortran编写并使用Mersenne Twister PRNG的Ladd's实现(该链接值得阅读)。在C中有很多合适的PRNG可以将状态暴露给您的控件。 PRNG看起来不错,并且使用它(在并行区域和私有(private)状态变量内进行初始化和destroy调用)可以使您获得不错的加速。
最后,通常情况下,如果您一口气要求完整的随机数序列,可以使PRNG性能更好(例如,编译器可以对PRNG内部进行矢量化处理)。因此,这些库通常具有类似于
get_prng_array(state)
函数的功能,这些函数可以将充满随机数的数组返回给您,就好像您将get_prng
放入填充数组元素的循环中一样-它们可以更快地完成操作。这将是第二次优化(并且需要在并行的for循环内添加一个for循环。显然,您并不想用完每个线程的堆栈空间!