将单线程应用程序迁移到多线程,并行执行,蒙特卡罗模拟

m3n*_*tat 8 c# parallel-processing multithreading threadpool

我的任务是采用现有的单螺纹蒙特卡罗模拟并对其进行优化.这是ac#console app,没有db访问它从csv文件加载一次数据并在最后写出来,所以它几乎只是CPU绑定,也只使用大约50mb的内存.

我通过Jetbrains dotTrace探测器运行它.在总执行时间中,大约30%生成均匀随机数,24%将均匀随机数转换为正态分布随机数.

基本算法是一大堆嵌套for循环,在中心有随机数调用和矩阵乘法,每次迭代返回一个加到结果列表中的double,这个列表定期排序并测试一些收敛标准(检查时)如果可以接受的话,程序会从循环中断开并写入结果,否则它会继续到最后.

我希望开发人员能够权衡:

  • 我应该使用新的Thread v ThreadPool
  • 我应该看一下Microsoft Parallels Extension库
  • 我应该看看AForge.Net Parallel.For,http://code.google.com/p/aforge/任何其他图书馆?

由于我从未编写任何并行或多线程代码,因此欢迎使用上述教程的一些链接.

  • 生成大量正态分布随机数的最佳策略,然后消耗它们.应用程序从未在此状态下使用统一随机数,它们始终转换为正态分布然后消耗.
  • 用于随机数生成的良好快速库(并行?)
  • 记忆考虑,因为我采取这种并行,我需要多少额外的东西.

当前应用程序需要2个小时进行500,000次迭代,业务需要将其扩展到3,000,000次迭代,并且每天被称为多次,因此需要进行一些繁重的优化.

特别想听听使用Microsoft Parallels ExtensionAForge.Net Parallel的人的意见

这需要相当快productionised所以.NET 4测试版出来,即使我知道它已经并发库烤,我们可以看看迁移到.NET 4后沿着轨道一旦它的发布.目前服务器有.Net 2,我已提交审核升级到我的开发箱所具有的.net 3.5 SP1.

谢谢

更新

我刚刚尝试了Parallel.For实现,但它提出了一些奇怪的结果.单线程:

IRandomGenerator rnd = new MersenneTwister();
IDistribution dist = new DiscreteNormalDistribution(discreteNormalDistributionSize);
List<double> results = new List<double>();

for (int i = 0; i < CHECKPOINTS; i++)
{
 results.AddRange(Oblist.Simulate(rnd, dist, n));
}
Run Code Online (Sandbox Code Playgroud)

至:

Parallel.For(0, CHECKPOINTS, i =>
        {
           results.AddRange(Oblist.Simulate(rnd, dist, n));
        });
Run Code Online (Sandbox Code Playgroud)

在模拟内部有很多调用rnd.nextUniform(),我想我得到的是很多相同的值,这是否可能发生,因为现在它是并行的?

也可能是List AddRange调用不是线程安全的问题?我明白了

System.Threading.Collections.BlockingCollection可能值得使用,但它只有Add方法没有AddRange所以我必须查看结果并以线程安全的方式添加.来自使用Parallel的人的任何见解.非常感谢.我暂时切换到System.Random我的调用因为我在使用我的Mersenne Twister实现调用nextUniform时遇到异常,也许它不是线程安全的某个数组正在使索引超出界限 ....

Mar*_*wis 13

首先,您需要了解为什么您认为使用多个线程是一种优化 - 实际上并非如此.只有拥有多个处理器时,使用多个线程才能使您的工作负载更快完成,然后最多只有您可用CPU的速度(这称为加速).传统意义上的工作没有"优化"(即工作量没有减少 - 事实上,对于多线程,由于线程开销,工作总量通常会增加).

因此,在设计应用程序时,您必须找到可以以并行或重叠方式完成的工作.有可能并行生成随机数(通过在不同的CPU上运行多个RNG),但这也会改变结果,因为您获得了不同的随机数.另一个选择是在一个CPU上生成随机数,在不同CPU上生成其他所有内容.这可以使您的最大加速比为3,因为RNG仍将按顺序运行,并且仍然需要30%的负载.

因此,如果你进行这种并行化,最终会得到3个线程:线程1运行RNG,线程2运行正态分布,线程3执行其余的模拟.

对于这种架构,生产者 - 消费者架构是最合适的.每个线程将从队列中读取其输入,并将其输出生成到另一个队列中.每个队列都应该是阻塞的,因此如果RNG线程落后,则规范化线程将自动阻塞,直到新的随机数可用.为了提高效率,我会在线程中传递100(或更大)数组中的随机数,以避免在每个随机数上进行同步.

对于此方法,您不需要任何高级线程.只需使用常规线程类,没有池,没有库.您唯一需要的是(遗憾的是)不在标准库中的是阻塞Queue类(System.Collections中的Queue类并不好).Codeproject提供了一个看起来合理的实现; 可能还有其他人.