对于c ++中的一些数值模拟,我需要生成许多具有指数分布的随机数(都具有相同的预定分布)。目前,我的程序运行良好,但超过50%的CPU时间用于生成这些随机数。
我想要做的是以不阻碍模拟主循环的方式生成这些随机数。更确切地说,我想有一个线程,其工作是始终保持随机数“提前准备好”,并在有人读取这个随机数时立即生成一个新的。
有人知道这样做的好方法吗?
目前,我的顺序代码如下所示:
#include <stdio.h>
#include <iostream>
#include <random>
using namespace std;
// exponential random variable with parameter lambda
class EXPGenerator{
exponential_distribution<> expo;
mt19937 engine; //mersene twister
public:
EXPGenerator(double lambda){
expo = exponential_distribution<>(lambda);
engine = mt19937(time(NULL));
}
double step(){
return expo(engine);
}
};
int main(int argc, char *argv[])
{
EXPGenerator expgen(2.0);
for(int i=0; i<100000; i++) {
double randv(expgen.step());
std::cout << randv << endl;
// do something complicated
}
return 0;
}
我使用clang++ -O2 --std=c++11 --stdlib=libc++ test.cpp -o test
[编辑:在上面添加-O2]
答案 0 :(得分:6)
您应该尝试的第一件事是启用优化。尝试在clang命令行中添加-O2选项。
答案 1 :(得分:6)
使用有界队列并让一个线程将随机数推入此队列,并在队列满时让该线程阻塞队列。要获取随机数,请从队列中取出一个数字,并在队列为空时让消费者线程阻塞队列。
当队列中有空间和cpu时间可用时,这个简单的设计将让生产者产生随机数。
优化:使用包含随机数列表的队列。在这种情况下,生产者将生成一个包含随机数的完整列表。消费者将使用队列中的列表保留缓存(可能在EXPGenerator内部)。一旦缓存为空,缓存将填充队列中的新列表。这将减少上下文切换开销,并且应当(原因)仅在测量显示时应用,这是有意义的。
队列应该基本上是一些std :: deque,其中T是随机数,或者std :: vector(随机数列表)。使用互斥锁同步对该std:队列的访问,并使用两个条件变量。一,表示有空间再次插入更多随机数。并且要发信号通知,队列中已经存在至少一个元素。让消费者等待第二个条件,当队列为空时让生产者等待第一个条件,当队列满了。
答案 2 :(得分:4)
当您使用优化时(正如其他人建议的那样),您可以在另一个线程中创建一堆随机数,将它们存储在向量中,并使用消息队列将其传输到主线程。在那里,您可以将其包装到EXPGenerator
。
答案 3 :(得分:2)
我可以在这里进行优化,但我认为还没有人提及过。
我没有看到等待随机数的消费者线程应该阻止等待生产者线程的任何原因。也就是说,如果随机数的缓存运行干,而不是阻塞,只需在消费者线程本身上生成一个或多个随机数,然后再次检查缓存。
不需要阻塞通信也可以更轻松地为线程间通信使用轻量级,无锁的数据结构。好的候选人包括:
事实上,如果你只有一个&#34;帮助线程&#34;,那么在一个生产者和一个消费者之间进行通信的特殊情况可以通过循环缓冲完成,而根本不需要任何原子内存操作。 / p>
答案 4 :(得分:1)
好的,首先创建随机生成线程。由于线程同步相对于生成一个随机数而言相对昂贵,因此加载一个向量(比如容量为10k),使用random(如Jan所示)是一个好主意。线程创建,终止和销毁也是一个PITA,所以循环“随机”线程绕过等待'go'AutoResetEvent,(参见MSDN),初始化为true-然后线程将在启动时生成一个random向量然后每当'发出'时都会发出信号。
在获得所有权之前,您需要一种机制来完成向量完全组装。您可以将它发布在生产者 - 消费者队列上,也可以按照Jan的建议将其发布到Windows消息队列中,这可能更容易(在这种情况下),只需从线程中获取向量即可。您可以使用另一个“完整”AutoResetEvent,初始化为false并等待它,随机线程在完成后发出信号。
当你拿一个向量时,发出'go'事件信号以启动随机线程生成另一个向量,以便以后你可能已经完成它。
您需要一个可以轻松转移所有权的矢量实例。我可能只是使用一个指针,在随机线程中创建一个new,生成randoms,在主线程中复制指针值并在完成后删除它。随机线程只要通过'go'就会新建另一个向量,因此重新设置自己的指针。如果你有一个合适的smart_ptr类,你可以使用它,可能是unique_ptr,因为它可以被移动。