关于启动大型多线程编程项目的建议

时间:2009-12-14 17:01:13

标签: multithreading architecture simulation parallel-processing

我的公司目前运行第三方模拟程序(自然灾难风险建模),从磁盘中吸取数GB的数据,然后压缩几天以产生结果。我很快就会被要求将其重写为一个多线程应用程序,以便它可以在几小时而不是几天内运行。我希望有大约6个月的时间来完成转换,并将独自工作。

我们有一个24-proc框来运行它。我可以访问原始程序的源代码(我认为是用C ++编写的),但在这一点上我对它的设计知之甚少。

我需要有关如何解决这个问题的建议。我是一名经验丰富的程序员(约30年,目前在C#3.5工作),但没有多处理器/多线程经验。如果合适的话,我愿意并渴望学习一门新语言。我正在寻找有关语言,学习资源,书籍,建筑指南的建议。等

要求:Windows操作系统。商业级编译器,提供大量支持和良好的学习资源。不需要花哨的GUI - 它可能从配置文件运行并将结果放入SQL Server数据库。

编辑:当前的应用程序是C ++,但我几乎肯定不会使用该语言进行重写。我删除了某人添加的C ++标记。

16 个答案:

答案 0 :(得分:17)

数字流程模拟通常在单个离散问题网格上运行(例如,surface of the Earthclouds of gas and dust),通常排除simple task farming或并发方法。这是因为在表示物理空间区域的一组处理器上划分的网格不是一组独立任务。每个子网格边缘的网格单元需要根据存储在逻辑空间中相邻的其他处理器上的网格单元的值进行更新。

high-performance computing中,模拟通常parallelised使用MPIOpenMP。 MPI是一个消息传递库,包含许多语言的绑定,包括C, C++, FortranPythonC#。 OpenMP是用于共享内存多处理的API。通常,MPI比OpenMP更难编码,并且更具侵入性,但也更灵活。 OpenMP需要处理器之间共享的内存区域,因此不适合许多体系结构。 Hybrid schemes也是可能的。

这种类型的编程有其特殊的挑战。除race conditionsdeadlockslivelocks以及concurrent programming的所有其他乐趣外,您还需要考虑处理器网格的topology - 您如何选择在物理处理器之间拆分逻辑网格。这很重要,因为您的并行speedup是处理器之间通信量的函数,它本身是分解网格总边长的函数。随着您添加更多处理器,此表面积会增加,从而增加communication overhead的数量。增加granularity最终将变得令人望而却步。

另一个重要的考虑因素是可以并行化的代码比例。 Amdahl's law然后决定理论上可达到的最大加速比。在开始编写任何代码之前,您应该能够对此进行估计。

这两个事实都会合谋限制您可以运行的最大处理器数量。甜蜜点可能比你想象的要低得多。

我推荐这本书High Performance Computing,如果你能抓住它的话。特别是,关于性能基准测试和调优的章节是无价的。

关于主要问题的并行计算的优秀在线概述是Lawerence Livermore National Laboratory的介绍。

答案 1 :(得分:12)

多线程项目中最大的问题是跨线程可以看到太多的状态 - 编写以不安全的方式读取/改变数据的代码太容易了,特别是在多处理器环境中,如缓存一致性等问题。一致的记忆等可能会发挥作用。

调试竞争条件显然令人不快。

如果您正在考虑在网络上的多台计算机上分配您的工作,那么请按照您的设计进行操作:即,确定哪些任务可以并行发生,每项任务的输入是什么,每项任务的输出是什么任务是,以及在给定任务开始之前必须完成哪些任务。这个练习的目的是确保每个地方的数据对另一个线程可见,以及每个产生新线程的地方都要仔细考虑。

一旦这样的初始设计完成,将对数据的所有权进行明确划分,并明确获得/转让所有权的点数;因此,您将处于非常有利的位置,可以利用多线程为您提供的各种可能性 - 廉价共享数据,廉价同步,无锁共享数据结构 - 安全。

答案 2 :(得分:7)

如果你可以将工作负载分成非依赖的工作块(即,数据集可以按比特处理,没有很多数据依赖性),那么我将使用线程池/任务机制。据推测,无论C#与Java的java.util.concurrent相当。我将从数据创建工作单元,并将它们包装在一个任务中,然后将任务抛给线程池。

当然,性能可能是必要的。如果您可以按原样保留原始处理代码内核,则可以在C#应用程序中调用它。

如果代码具有大量数据依赖性,那么分解为线程化任务可能要困难得多,但您可能会将其分解为一系列操作。这意味着线程1将数据传递给线程2,线程2将数据传递给线程3到8,线程3到8将数据传递到线程9等。

如果代码有很多浮点数学,那么在OpenCL或CUDA中重写并在GPU而不是CPU上运行它可能是值得的。

答案 3 :(得分:3)

如果你为它设计项目,有很多技术可以用来处理多线程。

最普遍和最普遍的只是“避免共享状态”。尽可能在线程之间复制资源,而不是让它们访问相同的共享副本。

如果您自己编写低级同步代码,则必须记住绝对不做任何假设。编译器和CPU都可能重新排序代码,创建竞争条件或死锁,在读取代码时似乎没有。防止这种情况的唯一方法是使用内存屏障。请记住,即使是最简单的操作也可能受到线程问题的影响。像++i这样简单的东西通常不是原子的,如果多个线程访问i,你将得到不可预测的结果。 当然,仅仅因为您已为变量赋值,这并不能保证新值对其​​他线程可见。编译器可能会推迟将其写入内存。同样,内存屏障强制它“刷新”所有挂起的内存I / O.

如果我是你,如果可能的话,我会使用更高级别的同步模型,而不是简单的锁/互斥锁/监视器/关键部分。有一些CSP库可用于大多数语言和平台,包括.NET语言和本机C ++。

这通常会使竞争条件和死锁无法检测和修复,并且可以实现荒谬的可扩展性。但是这个范例也存在一定的开销,因此每个线程可能比其他技术完成的工作少。它还要求整个应用程序专门针对这种范例进行构建(因此,对现有代码进行改造是很棘手的,但是因为你从头开始,它不是一个问题 - 但它仍然是你不熟悉的)< / p>

另一种方法可能是Transactional Memory。这更容易融入传统的程序结构,但也有一些限制,我不知道它的许多生产质量库(STM.NET最近发布,可能值得一试。英特尔有一个C ++带有STM扩展的编译器也包含在语言中)

但无论您使用哪种方法,您都必须仔细考虑如何将工作分解为独立任务,以及如何避免线程之间的串扰。任何时候两个线程访问同一个变量,你就有一个潜在的bug。任何时候两个线程访问相同的变量或只是相同地址附近的另一个变量(例如,数组中的下一个或前一个元素),必须在核心之间交换数据,强制它到从CPU缓存刷新到内存,然后读入另一个核心的缓存。这可能是一个重大的性能影响。

哦,如果你用C ++编写应用程序,不要低估语言。在能够编写健壮的代码之前,您必须详细学习该语言,而不那么强大的线程代码。

答案 4 :(得分:3)

对于一个为期6个月的项目,我会说首先要开始阅读一本关于这个主题的好书,这肯定是值得的。我建议Joe Duffy's Concurrent Programming on Windows。这是我所知道的关于该主题的最全面的书,它涵盖了.NET和本机Win32线程。当我发现这个宝石时,我已经写了10年的多线程程序,并且几乎每章都找到了我不知道的东西。

此外,“自然灾难风险建模”听起来像很多数学。也许您应该看一下英特尔的IPP库:它为许多常见的低级数学和信号处理算法提供原语。它支持开箱即用的多线程,这可以使您的任务变得更加容易。

答案 5 :(得分:2)

我们在这种情况下所做的一件事对我们来说非常有效,就是将要完成的工作分解为单个块以及将每个块上的操作分解到不同的处理器中。然后我们有处理器链和数据块可以独立地通过链工作。链中的每组处理器可以在每个线程上运行,并且可以根据自身相对于链中其他处理器的性能处理更多或更少的数据。

同时将数据和操作分解为更小的部分使得应用程序更易于维护和测试。

答案 6 :(得分:2)

这里可以提供大量具体的个人建议,而且有几个人已经这样做了。 然而,没有人能够确切地告诉你如何使这一切能够满足你的特定要求(你甚至还不完全了解自己),所以我强烈建议你现在就阅读HPC (High Performance Computing)以获得结束-arching概念清晰,更好地了解哪个方向最适合您的需求。

答案 7 :(得分:2)

特别了解Erlang和“Actor Model”。如果您使所有数据都不可变,那么您将更容易并行化它。

答案 8 :(得分:2)

您选择使用的模型将取决于您的数据结构。您的数据是紧密耦合还是松散耦合?如果您的模拟数据紧密耦合,那么您将需要查看OpenMP或MPI(并行计算)。如果您的数据松散耦合,那么工作池可能更合适......甚至可能采用分布式计算方法。

我的建议是获取并阅读介绍性文本,以熟悉各种并发/并行模型。然后查看应用程序的需求并确定您将需要使用的架构。在了解了所需的架构之后,您可以查看可以帮助您的工具。

作为该主题介绍的一本评价相当高的书是“并发的艺术:线程猴子编写并行应用程序指南”。

答案 9 :(得分:1)

其他大多数答案都提供了有关分区项目的好建议 - 查找可以并行执行的任务,只需要很少的数据共享。请注意非线程安全的构造,例如静态或全局变量,或非线程安全的库。我们遇到的最糟糕的是TNT库,在某些情况下甚至不允许线程安全读取。

与所有优化一样,首先要集中精力解决瓶颈问题,因为线程会增加许多复杂性,以避免在没有必要的情况下使用它。

您需要很好地掌握各种线程原语(互斥,信号量,关键部分,条件等)以及它们有用的情况。

如果您打算继续使用C ++,我想补充的一点是,我们使用boost。线程库取得了很大的成功。它提供了大多数所需的多线程原语,虽然缺少一个线程池(我会警惕可以通过谷歌定位的非官方“提升”线程池,因为它会遇到许多死锁问题)。 p>

答案 10 :(得分:1)

我会考虑在.NET 4.0中这样做,因为它有很多新的支持,专门用于简化编写并发代码。它的正式发布日期是2010年3月22日,但在此之前它可能是RTM,你现在可以从相当稳定的Beta 2开始。

您可以使用您更熟悉的C#,也可以使用托管C ++。

在较高的层面上,尝试将程序分解为System.Threading.Tasks.Task,这是单独的工作单元。此外,我会尽量减少对共享状态的使用,并尽可能考虑使用Parallel.For(或ForEach)和/或PLINQ

如果你这样做,将以非常有效的方式为你完成很多繁重的工作。这是微软将越来越多地支持的方向。

2:我会考虑在.NET 4.0中这样做,因为它有很多新的支持,专门用于简化编写并发代码。它的正式发布日期是2010年3月22日,但在此之前它可能是RTM,你现在可以从相当稳定的Beta 2开始。在较高的层面上,尝试将程序分解为System.Threading.Tasks.Task,这是单独的工作单元。另外,我最小化共享状态的使用并考虑尽可能使用Parallel.For和/或PLINQ。如果你这样做,将以非常有效的方式为你完成很多繁重的工作。 1http://msdn.microsoft.com/en-us/library/dd321424%28VS.100%29.aspx

答案 11 :(得分:1)

很抱歉,我只想在这里添加一个悲观或更真实的答案。

你的时间紧迫。 6个月的截止日期,你甚至不知道这个系统是什么语言,它做了什么以及如何组织。如果这不是一个微不足道的计算,那么这是一个非常糟糕的开始。

最重要的是:你说你以前从未做过多元编程。这是我一次响铃4个闹钟的地方。多线程是很困难的,当你想要做正确的事情时需要花费很长时间来学习它 - 当你想要获得巨大的速度提升时,你需要做正确的事情。即使使用Total Views debugger或Intels VTune等优秀工具,调试也非常糟糕。

然后你说你想要在另一个语言中重写应用程序 - 这并不像你必须重写它那么糟糕。将单线程程序转换为运行良好的多线程程序而不进行全面重新设计的机会几乎为零。

但学习多线程和一种新语言(你的C ++技能是什么?)时间轴为3个月(你必须写一个扔掉的原型 - 所以我将时间跨度减少到两半)是极具挑战性的。

我的建议很简单,不会喜欢它:现在学习多线程 - 因为它是未来所需的技能 - 但是把这份工作留给已经有经验的人。好吧,除非你不关心程序是否成功,只是在寻找6个月的付款。

答案 12 :(得分:0)

如果可以让所有线程处理不相交的过程数据集,并将其他信息存储在SQL数据库中,那么您可以很容易地在C ++中完成它,并且只是生成新线程来处理它们自己的部分使用Windows API。 SQL服务器将通过其数据库事务处理所有硬同步魔法!当然,C ++的执行速度比C#快很多。

您绝对应该为此任务修改C ++,并理解C ++代码,并在现有代码中查找效率错误以及添加多线程功能。

答案 13 :(得分:0)

您已将此问题标记为C ++,但提到您目前是C#开发人员,因此我不确定您是否要从C ++或C#处理此任务。无论如何,如果您将要使用C#或.NET(包括C ++ / CLI):我将以下MSDN文章加入书签,并强烈建议您阅读它作为准备工作的一部分。

Calling Synchronous Methods Asynchronously

答案 14 :(得分:0)

无论你要写什么技术,看看this must read book on concurrency "Concurrent programming in Java"和.Net我强烈推荐retlang library并发应用。

答案 15 :(得分:0)

我不知道它是否已被提及,但如果我在你的鞋子里,我现在要做的事情(除了阅读这里发布的每个答案)都是在你最喜欢的(多数)编写多线程示例应用程序用过的)语言。

我没有广泛的多线程经验。我过去玩过它很有趣但是我觉得获得一些扔掉应用程序的经验将适合你未来的努力。

我祝你好运,我必须承认我希望我有机会在这样的事情上工作......