我试着写一些可以非常快速地计算随机数的东西,并且可以应用于多个线程。我目前的代码是:
/* 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()是否适用于多线程代码?如果不是,那还有一个好的选择吗?非常感谢。千斤顶
答案 0 :(得分:7)
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看起来很好并且使用它(在并行区域和私有状态变量中使用初始化和销毁调用)应该会给你一个不错的加速。
最后,如果你一次性要求完整的随机数序列(例如编译器可以对PRNG内部进行矢量化),通常可以使PRNG的性能更好。因为这个库通常有类似get_prng_array(state)
函数的东西,它们会给你一个充满随机数的数组,好像你把get_prng
放在一个填充数组元素的循环中 - 它们只是更快地完成它。这将是第二次优化(并且需要在并行for循环中添加for循环。显然,你不希望用完每个线程的堆栈空间!