【问题标题】:Generate random numbers in advance in another thread in c++11在c++11的另一个线程中预先生成随机数
【发布时间】:2013-06-25 07:14:25
【问题描述】:

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

【问题讨论】:

  • 您为什么要根据未优化构建的性能做出决策?在您寻求任何复杂且难以维护的解决方案之前,请测试您是否真的有真正的问题。启用优化后,代码可能会足够快。
  • 上面的程序只是一个例子,我的问题实际上是基于某些东西。我已经尽可能地优化了真实的程序,它仍然需要几个小时才能运行......我们正在谈论模拟随机扩散过程,这需要数千个时间步长和数千次模拟才能获得良好的统计数据......
  • 您正在编译但未启用优化
  • 确实如此,上面的命令行也是如此。但真正的程序(这里显示太长)是用-O2 编译的。抱歉,我应该将 -O2 放在我原来的问题中。
  • 很公平。 :) 是的,在最初的问题中表明这一点可以避免一些混淆但是只要你的测量基于优化的构建,这就是主要的事情;)

标签: c++ multithreading random c++11


【解决方案1】:

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

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

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

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

【讨论】:

    【解决方案2】:

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

    【讨论】:

    • 是的,我已经尝试过-O2,即使它确实会减少一些因素的执行时间,实际模拟仍然需要几个小时,通过生成随机数可能会减少一半在另一个线程中。
    • 我应该把它放在问题中,对不起。
    【解决方案3】:

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

    【讨论】:

    • 是的,这就是我想做的。不幸的是,我不知道如何实现它。我来看看消息队列是如何工作的。
    • @Nown 队列基本上应该是一些 std::deque ,其中 T 是一个随机数,或 std::vector (随机数列表)。使用互斥锁来同步对该 std:queue 的访问并使用两个条件变量。一,表示有空间再次插入更多随机数。还有一个信号是,队列中已经至少有一个元素。让消费者等待第二个条件,当队列为空时,让生产者等待第一个条件,当队列满时。
    【解决方案4】:

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

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

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

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

    【讨论】:

      【解决方案5】:

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

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

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

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

      【讨论】:

      • C++11 中有互斥量和条件变量,因此无需使用 windows 结构。为什么要在 std::vector 上使用指针?您可以在两个线程之间轻松交换它们。
      • @TorstenRobitzki - 是的,好吧,随便:)。任何避免复制ctor的东西。另外,我刚刚在看一个winapi问题时误读了标签:(
      猜你喜欢
      • 2012-02-07
      • 1970-01-01
      • 2015-10-30
      • 2013-03-15
      • 1970-01-01
      • 2013-11-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多