我有一个小程序,使用各种卡计数策略实现BlackJack的蒙特卡罗模拟。我的主要功能基本上是这样做的:
int bankroll = 50000;
int hands = 100;
int tests = 10000;
Simulation::strategy = hi_lo;
for(int i = 0; i < simulations; ++i)
runSimulation(bankroll, hands, tests, strategy);
整个程序在我的机器上的单个线程中运行大约需要10秒钟。
我想利用我的处理器所拥有的3个内核,所以我决定重写程序,只需在不同的线程中执行各种策略:
int bankroll = 50000;
int hands = 100;
int tests = 10000;
Simulation::strategy = hi_lo;
boost::thread threads[simulations];
for(int i = 0; i < simulations; ++i)
threads[i] = boost::thread(boost::bind(runSimulation, bankroll, hands, tests, strategy));
for(int i = 0; i < simulations; ++i)
threads[i].join();
但是,当我运行此程序时,即使我得到相同的结果,也需要大约24秒才能完成。我在这里错过了什么吗?
答案 0 :(得分:5)
如果simulations
的值很高,那么最终会创建大量线程,这样做的开销最终会破坏任何可能的性能提升。
编辑:一种方法可能就是启动三个线程,让每个线程运行所需模拟的1/3。或者,使用某种线程池也可以提供帮助。
答案 1 :(得分:2)
这是具有线程池的工作队列的良好候选者。我已经使用英特尔线程模块(TBB)来满足这些要求。使用手工制作的线程池也可以快速入侵。在Windows上,操作系统为您提供了一个很好的线程池支持的工作队列 "QueueUserWorkItem()"
答案 2 :(得分:1)
阅读Herb Sutter的这些文章。你可能是“虚假分享”的受害者。
http://drdobbs.com/go-parallel/article/showArticle.jhtml?articleID=214100002
http://drdobbs.com/go-parallel/article/showArticle.jhtml?articleID=217500206
答案 3 :(得分:0)
我同意dlev。如果您的函数runSimulation没有更改下一次调用“runSimulation”以使其正常工作所需的任何内容,那么您可以执行以下操作:
。将“模拟”除以3。
。现在你将有3个计数器“0到模拟/ 3”“(模拟/ 3 + 1)到2模拟/ 3”和“(2 *模拟)/ 3 + 1到模拟”。
所有这3个计数器可以同时用于三个不同的线程。
**注意:: **您的要求可能根本不适合此类检查,以防您必须进行共享数据锁定
答案 4 :(得分:0)
我参加这个派对已经迟到了,但是想要为其他人发现两件事:
1)绝对看到David指出的第二个Herb Sutter链接(http://www.drdobbs.com/parallel/eliminate-false-sharing/217500206)。它解决了引发我这个问题的问题,概述了一个结构数据对象包装器,它确保单独的并行线程不会竞争总部位于同一内存缓存线上的资源(硬件控制将阻止多个线程访问相同的内存缓存 - 同时排队)。
2)重新提出原始问题,dlev指出了问题的很大一部分,但由于这是一个模拟我敢打赌,这是一个更深层次的问题。虽然您的程序的高级变量都没有共享,但您可能有一个共享的关键系统变量:系统级“最后一个随机数”,它存储在引擎盖下并用于创建下一个随机数。您甚至可能为每个模拟初始化专用的生成器对象,但如果他们正在调用rand()这样的函数,那么他们以及他们的线程会重复调用相同的共享系统资源并随后相互阻塞。问题#2的解决方案取决于模拟程序本身的结构。例如,如果对随机生成器的调用被分段,那么我可能会批量处理一个前期调用,该调用将检索并存储模拟所需的内容。现在,我想知道更复杂的方法是否能解决潜在的随机生成共享资源问题......