我想在计算集群上同时运行多个实例的代码(大约2000个实例)。其工作方式是,我提交作业,集群将在节点定期开放时运行它们,每个节点可运行多个作业。这似乎会导致许多实例在随机数生成中使用时间种子时产生相同的值。
是否有简单的替代方案可供使用?可复制性和安全性并不重要,快速生成唯一种子很重要。最简单的方法是什么,如果可能的话,跨平台的方法会很好。
我想在计算集群上同时运行多个实例的代码(大约2000个实例)。其工作方式是,我提交作业,集群将在节点定期开放时运行它们,每个节点可运行多个作业。这似乎会导致许多实例在随机数生成中使用时间种子时产生相同的值。
是否有简单的替代方案可供使用?可复制性和安全性并不重要,快速生成唯一种子很重要。最简单的方法是什么,如果可能的话,跨平台的方法会很好。
rdtsc
指令是一个相当可靠(且随机)的种子。
在Windows中,可以通过__rdtsc()
内建函数来访问该指令。
在GNU C中,可以通过以下方式访问:
unsigned long long rdtsc(){
unsigned int lo,hi;
__asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi));
return ((unsigned long long)hi << 32) | lo;
}
该指令测量自处理器上电以来的总伪周期数。鉴于当今机器的高频率,即使两个处理器在相同时间启动并以相同速度时钟定时,它们返回相同值的可能性极小。
将PID和时间组合起来应该足以获得唯一的种子。这不是100%跨平台的,但是在*nix平台上使用getpid(3)
,在Windows上使用GetProcessId
可以让你成功99.9%。下面这个示例代码可能会有所帮助:
srand((time(NULL) & 0xFFFF) | (getpid() << 16));
您也可以从*nix系统的/dev/urandom
读取数据,但是Windows上没有相当于此的等效物。
unsigned seed;
read(open("/dev/urandom", O_RDONLY), &seed, sizeof seed);
srand(seed); // IRL, check for errors, close the fd, etc...
std::random_device
。建议您观看链接以获得全面指南。srand
和rand
,而应该使用std::random_device
和std::mt19937
。对于大多数情况,以下内容是您想要的:#include <iostream>
#include <random>
int main() {
std::random_device rd;
std::mt19937 mt(rd());
std::uniform_int_distribution<int> dist(0,99);
for (int i = 0; i < 16; i++) {
std::cout << dist(mt) << " ";
}
std::cout << std::endl;
}
提供 CryptGenRandom()
和 RtlGenRandom()
函数。它们将返回一个随机字节数组,您可以将其用作种子。
在 Linux 上,您可以使用 Openssl 的 RAND_bytes()
函数获取一定数量的随机字节。默认情况下,它会使用 /dev/random
。
#ifdef _WIN32
#include <NTSecAPI.h>
#else
#include <openssl/rand.h>
#endif
uint32_t get_seed(void)
{
uint32_t seed = 0;
#ifdef _WIN32
RtlGenRandom(&seed, sizeof(uint32_t) );
#else
RAND_bytes(&seed, sizeof(uint32_t) );
#endif
return seed;
}
与其使用 C 标准库的 time() 函数测量秒数,您是否可以改用处理器的计数器呢?大多数处理器都拥有一个自由运行的时钟周期计数器,例如 x86/x64 上有时间戳计数器:
时间戳计数器是自 Pentium 以来在所有 x86 处理器上存在的 64 位寄存器。它记录从复位开始的时钟周期数。
(该页面还有许多不同平台访问此计数器的方式 - gcc/ms visual c 等)
请注意,时间戳计数器并非没有缺陷,它可能无法在处理器之间同步(您可能不关心应用程序)。并且节能功能可能会加速或减慢处理器速度(同样,您可能不关心)。
一个想法... 生成一个 GUID(16字节),并对它的4字节或8字节块进行求和(取决于种子期望的宽度),允许整数环绕。将结果用作种子。
GUID通常封装了生成它们的计算机的特征(例如MAC地址),这应该使得两台不同的计算机最终生成相同的随机序列的概率非常小。
显然,这并不具有可移植性,但是对于您的系统找到适当的API /库不应太难(例如Win32上的UuidCreate
,Linux上的uuid_generate
)。
假设您在一个相当 POSIX-ish 的系统上,应该有 clock_gettime
。这将给出纳秒的当前时间,这意味着从实际目的来说不可能获得两次相同的值。(理论上,糟糕的实现可能会有较低的分辨率,例如只将毫秒乘以一百万,但即使像 Linux 这样的半好的系统也会给出真正的纳秒结果)。
std::random_device
来种子化随机数生成器。 - EmeryBerger