我正在编写一个应该有点快的数值程序,而且也是多线程的。我有一个类表示一个数字,我想在其中使用一个随机数生成器。现在,我并不真的要求我的RNG是一个真正的RNG,我只需要它生成均匀分布在0到NMAX之间的整数。
所以我的班上有这样一个:
// use just an int here and forget about multithreading.
static uint32 rand = NMAX/4;
// this will be called multithreadedly
static uint32 GetRand() { return rand = ( rand + 1 ) % NMAX; }现在,在一个线程化的世界中,这对我的目的来说是完全没有问题的。
因为这是多线程的,所以我假设唯一可能发生的坏事情是偶尔(比如<1%的时间)更新会被删除。这意味着两个线程读取rand,在寄存器中更新它,返回更新后的值,然后用相同的值写两次。这完全没问题。
我的问题是比这更糟糕的事还会发生吗?我完全同意每个线程使用自己的rand变量,但这只是一个巨大的痛苦来实现。我绝对不能做的是让类的每个实例都使用自己的rand变量,因为这样会占用太多的内存。
更新:
那我为什么要这么做?完整的故事是一个浮点类,它使用1或2个字节。所以它必须是快速的,这似乎是最好的方法。事实上,我认为我会将它从( rand + 1 ) % NMAX更新到类似( rand + [some prime] ) % NMAX的东西,因为它似乎更好用。这是一个例子,在这种情况下,一个更健壮的解决方案需要更多的代码,使代码更少通用,更依赖,使代码变得不那么清晰和更容易中断,所有这些都是因为“应该使用适当的同步”。
最主要的是,我担心编译器可能会做一些奇怪的优化,这样对rand的更新不仅会被删除,而且会变成完全的垃圾。不过,现在我已经考虑过了,即使这样也可以(使用这个数字的方式),因为下一次使用GetRand将使它%NMAX,这个错误最多只会导致GetRand的一次使用超出[0,NMAX]的给定范围。谢谢你的回答。
发布于 2010-08-22 05:46:04
我完全同意每个线程使用自己的
rand变量,但这只是一个巨大的痛苦来实现。
用这种方式做并不一定那么困难。一些编译器(例如GCC)支持http://en.wikipedia.org/wiki/Thread_local_storage#GCC,它允许每个线程都有自己的给定变量的副本。
话虽如此,用你目前的做法,我只能想到一个问题--除了你提到的那个问题。如果每个线程运行在不同的内核上,并且随机值存储在每个内核的非共享缓存中,那么更新可能无法在内核之间传播不确定的时间。您可以通过使用记忆屏障 (可以通过使用锁来创建)来避免这种情况,但是这可能不利于性能。
发布于 2010-08-22 06:37:28
为了便于讨论,我们假设以下实现:
我对改进的建议是,每个实例一次获取16个数字。不需要在实例中存储(复制)这16个数字:只需将全局索引增加16 (使其他实例不可用),以便实例可以逐一使用它们。
发布于 2010-08-22 07:35:36
__declspec(thread) static uint32 rand = NMAX/4;
// this will be called multithreadedly
static uint32 GetRand() { return rand = ( rand + 1 ) % NMAX; }static long rand = NMAX/4;
// this will be called multithreadedly
static uint32 GetRand() { return InterlockedIncrement(&rand) % NMAX; }https://stackoverflow.com/questions/3540331
复制相似问题