我正在努力寻找为我正在研究的MC模拟生成随机数的最有效方法。我一直在阅读关于双精度Mersenne Twister算法的很多内容,我想在继续之前了解一些基本的东西。
我编译并运行官方dSFMT文件提供的测试,这对我的系统来说是最好的结果:
C:\TDM-GCC-64\C++ Tests\dSFMT>test-sse2-M19937 -s
consumed time for generating 100000000 randoms.
ST BLOCK [0, 1) AVE: 115ms.
ST BLOCK (0, 1] AVE: 108ms.
ST BLOCK (0, 1) AVE: 106ms.
ST BLOCK [1, 2) AVE: 77ms.
ST SEQ [0, 1) 1 AVE: 174ms.
ST SEQ [0, 1) 2 AVE: 207ms.
total = 500014655.815776
ST SEQ (0, 1] 1 AVE: 173ms.
ST SEQ (0, 1] 2 AVE: 205ms.
total = 500035344.184224
ST SEQ (0, 1) 1 AVE: 209ms.
ST SEQ (0, 1) 2 AVE: 247ms.
total = 500014655.815776
ST SEQ [1, 2) 1 AVE: 173ms.
ST SEQ [1, 2) 2 AVE: 204ms.
total = 1500064655.815183
我的问题是:
答案 0 :(得分:2)
库内的数字是从[1,2]间隔生成的。其他范围表示为此间隔之上的函数。
“基本”区间[1,2]生成器:
inline static double dsfmt_genrand_close1_open2(dsfmt_t *dsfmt) {
double r;
double *psfmt64 = &dsfmt->status[0].d[0];
if (dsfmt->idx >= DSFMT_N64) {
dsfmt_gen_rand_all(dsfmt);
dsfmt->idx = 0;
}
r = psfmt64[dsfmt->idx++];
return r;
}
间隔[0,1):
inline static double dsfmt_genrand_close_open(dsfmt_t *dsfmt) {
return dsfmt_genrand_close1_open2(dsfmt) - 1.0;
}
由于许多原因,块生成可能更快,包括缓存局部性,较少的函数调用,循环展开等。在实践中,块操作通常比单个操作组合更快。
在这种特殊情况下,块生成也更快,因为数字是成对生成的(W128_T
类型):
union W128_T {
__m128i si;
__m128d sd;
uint64_t u[2];
uint32_t u32[4];
double d[2];
};
块版本使用此属性,并将W128_T
中的两个数字复制到结果数组中。顺序版仅使用第一个数字并丢弃第二个数字。
至于你的第三个问题,使用块生成,因为它在你的计算机上被证明更快。每100毫秒有1e8个数字,因此对于1e12,你需要大约20分钟。如果你没问题,那么只需使用NUM_RANDS
块大小,对于任何合理的块大小应该没有太大区别。否则,请考虑从多个线程中的独立生成器生成数字。