6

对于 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]

4

5 回答 5

6

您应该尝试的第一件事是启用优化。尝试在 clang 命令行中添加 -O2 选项。

于 2013-06-25T07:22:52.913 回答
6

使用有界队列并让一个线程将随机数推送到该队列中,并在队列已满时让该线程阻塞在队列中。要获取随机数,请从该队列中拉出一个数,并在队列为空时让消费者线程阻塞队列。

这个简单的设计将让生产者在队列中有空间并且cpu时间可用时产生随机数。

优化:使用带有随机数列表的队列。在这种情况下,生产者将生成一个包含随机数的完整列表。消费者将保留一个缓存(可能在 EXPGenerator 内部),其中包含队列外的列表。一旦缓存为空,缓存将被队列中的新列表填充。这将减少上下文切换开销,并且应该(当然)仅在测量显示时应用,这是有意义的。

队列基本上应该是一些 std::deque ,其中 T 是一个随机数,或 std::vector (随机数列表)。使用互斥锁来同步对该 std:queue 的访问并使用两个条件变量。一,表示有空间再次插入更多随机数。还有一个信号,表明队列中已经至少有一个元素。让消费者等待第二个条件,当队列为空时,让生产者等待第一个条件,当队列满时。

于 2013-06-25T08:00:40.573 回答
4

当您使用优化(如其他人建议的那样)时,您可以在另一个线程中创建一堆随机数,将它们存储在一个向量中,并使用消息队列将其传输到您的主线程。在那里你可以将它包装到你的EXPGenerator.

于 2013-06-25T07:31:43.890 回答
2

这里有一个可能的优化,我认为还没有人提到过。

我看不出有任何理由等待随机数的消费者线程应该阻塞在生产者线程上的等待。也就是说,如果随机数缓存干涸,而不是阻塞,只需在消费者线程本身上产生一个或多个随机数,然后再次检查缓存。

不需要阻塞通信也使得使用轻量级、无锁数据结构进行线程间通信变得更加容易。好的候选人包括:

事实上,如果你只有一个“辅助线程”,那么在一个生产者和一个消费者之间进行通信的特殊情况可以通过一个循环缓冲区来完成,而根本不需要任何原子内存操作。

于 2014-04-10T08:34:34.327 回答
1

好的,首先创建您的随机生成线程。由于与生成一个随机数相比,线程同步相对昂贵,因此加载一个带有随机数的向量(例如容量为 10k)(如 Jan 所建议的)是一个好主意。线程创建、终止和销毁也是一个 PITA,因此循环“随机”线程循环等待“开始”AutoResetEvent,(参见 MSDN),初始化为真 - 然后线程将在启动时生成一个随机向量,然后每当发出“go”信号时。

您需要一种机制来等待向量完全组装,然后再获得它的所有权。您可以将它发布在生产者-消费者队列上,可能是 Windows 消息队列,正如 Jan 所建议的那样,可能更容易(在这种情况下),在完成后从线程中获取向量。您可以使用另一个“完成” AutoResetEvent,初始化为 false 并等待它,随机线程在完成时发出信号。

获取向量后,立即发出“go”事件以启动随机线程生成另一个向量,以便以后需要它时它可能已经完成。

您需要一个可以轻松转移所有权的向量实例。我可能只使用一个指针,在随机线程中创建一个新的,生成随机数,在主线程中复制指针值并在完成后将其删除。每当它通过“go”时,随机线程将只是新的另一个向量,因此重新定位它自己的指针。如果你有一个合适的 smart_ptr 类可用,你可以使用它,可能是 unique_ptr,因为它可以移动。

于 2013-06-25T08:16:29.200 回答