多核系统编程和编译的现状

6
我正在研究多核处理器,具体来说,我正在研究编写多核处理器代码以及为多核处理器编译代码。我对这个领域的主要问题感到好奇,这些问题目前可能会阻止广泛采用编程技术和实践,以充分利用多核架构的性能。
我知道以下努力(其中一些似乎与多核架构没有直接关系,但似乎更多涉及并行编程模型、多线程和并发):
  • Erlang(我知道Erlang包含构造以促进并发,但我不确定它如何被利用于多核架构)
  • OpenMP(似乎主要与多处理和利用集群的能力有关)
  • Unified Parallel C
  • Cilk
  • Intel Threading Blocks(这似乎直接与多核系统相关;由英特尔提供。除了定义某些编程结构之外,它还具有一些功能,可以让编译器为多核架构优化代码)
一般来说,从我很少接触到的多线程编程经验来看,考虑并发和并行编程绝对是一个困难的概念。我也知道多线程编程和多核编程是两回事。在多线程编程中,您确保CPU不会闲置(在单CPU系统上)。正如James指出的那样,操作系统可以安排不同的线程在不同的核心上运行,但我更感兴趣的是通过语言本身或编译器描述并行操作。据我所知,您无法真正执行并行操作。在多核系统中,您应该能够执行真正的并行操作。
因此,在我看来,目前面临的多核编程问题包括:
- 多核编程是需要具有显著技能的困难概念。 - 当今的编程语言中没有原生结构提供良好抽象以为多核环境编程。 - 除了英特尔的TBB库外,我没有找到其他编程语言利用多核架构进行编译的努力(例如,我不知道Java或C#编译器是否针对多核系统优化字节码,甚至不知道JIT编译器是否这样做)。
我想知道其他可能存在的问题,以及是否有正在开发的解决方案来解决这些问题。提供研究论文(等类似资料)的链接将非常有帮助。谢谢!
编辑:
如果我必须把我的问题简化为一个句子,那就是:多核编程面临哪些问题,目前有哪些研究在进行以解决这些问题?
更新:
我认为多核需要关注三个层面:
1. 语言层面:抽象并行和并发的构造/概念/框架,使程序员更容易表达。
2. 编译器层面:如果编译器知道它正在编译的体系结构,它可以优化编译后的代码以适应该体系结构。
3. 操作系统层面:操作系统优化运行进程,可能对不同的线程/进程安排在不同的核心上运行。

我在ACM和IEEE上搜索了一些论文。其中大部分讨论了并发思考的困难以及当前语言没有适当的方式来表达并发。有些人甚至声称我们现有的并发模型(线程)不是处理并发的好方法(即使在多核情况下)。我很想听听其他观点。


你能解释一下为并行编程模型和多线程设计相对于为多核编程而言有何不同吗? - James Black
如果您的编程语言支持多核,且您的操作系统具有良好的支持,它可以确保线程在不同的内核或处理器上运行,因此您的多线程应用程序可以并发运行。 - James Black
你可能想阅读https://dev59.com/bHVC5IYBdhLWcg3wykej并更好地理解这个主题。 - James Black
@James 我认为并行编程和多核编程比多线程和多核编程更相似。我应该在我的问题中提到我是在单CPU系统上讨论多线程的。我可以看到操作系统如何安排不同的线程在不同的核心上运行,但我更感兴趣的是使用语言/编译器本身来描述并行性。 - Vivin Paliath
@James,我的意思是要讲得非常笼统,但空间很快就不够了。通用的线程模型,例如pthread或Windows线程,默认情况下没有这样的处理器亲和力,并且会在所有可用的核心/处理器上调度线程,除非您另有指示。 - Nathan Ernst
显示剩余3条评论
5个回答

5
我对这个领域的主要问题感到好奇,目前有什么阻碍广泛采用编程技术和实践,以充分利用多核架构的强大功能。
惯性。(顺便说一句:这几乎是回答所有“什么阻止了广泛采用”的问题的答案,无论是并行编程模型、垃圾收集、类型安全还是燃油效率高的汽车。)
自20世纪60年代以来,我们就知道线程+锁模型根本有问题。到了1980年左右,我们已经有了大约十几种更好的模型。然而,今天使用的绝大多数语言(包括在1980年之后新创建的语言),仍然只提供线程+锁。

谢谢!哪些模型更好?我猜你是在谈论演员模型和“承诺和未来”?我看到这在并发/协调语言中非常普遍。 - Vivin Paliath
1
@Vivin Paliath:join-calculus,π-calculus,actor model,CSP,一般的消息传递,数据流,嵌套数据并行性,软件事务性内存,一般事务,代理...有很多种,并且我们还没有找到哪些是起作用的。我们只知道线程+锁*不起作用。只要我们还没想出来,我喜欢Clojure模型:提供一个合理的更新模型,构建许多不同的并发模型(我相信现在Clojure 1.2中有6个)。 - Jörg W Mittag

4
多核编程的主要问题与编写任何其他并发应用程序的问题相同,但以前在计算机中拥有多个CPU是不常见的,现在很难找到只有一个内核的现代计算机,因此,为了利用多核、多CPU架构,有新的挑战。但是,这个问题是一个旧问题,每当计算机体系结构超出编译器时,回退解决方案似乎是向函数式编程靠近,因为如果严格遵循该编程范例,则可以制作非常可并行化的程序,例如没有任何全局可变变量。但是,并非所有问题都可以轻松使用FP解决,因此目标是如何使其他编程范例在多核上易于使用。首先,许多程序员已经避免编写良好的多线程应用程序,因此没有强大的开发人员准备好,因为他们学习了使编码更加困难的习惯。但是,对于大多数CPU的更改,您可以查看如何更改编译器,对此,您可以查看Scala、Haskell、Erlang和F#。对于库,您可以查看MS的并行框架扩展,作为一种简化并发编程的方法。
在工作中,我最近看到IEEE Spectrum或IEEE Computer上有关于多核编程问题的文章,因此可以查看IEEE和ACM关于这些问题所写的文章,以获取更多想法。
我认为最大的障碍将是让程序员改变他们的语言,因为FP与OOP非常不同。
除了开发适用于此方式的语言之外,还有一个研究领域是如何处理多个线程访问内存,但是,就像这个领域的许多内容一样,Haskell似乎在测试这方面的想法方面处于前沿,因此您可以了解一下Haskell的情况。
最终将会有新的语言出现,也许我们会有DSL来帮助抽象开发人员,但是如何教育程序员将是一个挑战。
更新:你可能会对第24章《并发和多核编程》感兴趣,http://book.realworldhaskell.org/read/concurrent-and-multicore-programming.html

非常感谢--我现在正在查看ACM和IEEE的文章。另外,感谢您提供Haskell书籍的链接。 - Vivin Paliath
接受这个答案。希望我也能接受Jörg的答案!两个答案都很好且信息丰富。 - Vivin Paliath

3

有一个答案提到了.NET框架的并行扩展,由于你提到了C#,这绝对是我会调查的东西。微软在这方面做了一些有趣的事情,但我认为他们的许多努力似乎更适合于增强C#语言而不是用于并发编程的单独和独特的库。但我认为他们的努力值得赞扬,也尊重我们现在处于早期阶段。(免责声明:我曾经是大约3年前Visual Studio的市场总监)

英特尔线程构建块也非常有趣(英特尔最近发布了新版本,我很兴奋地参加了下周的英特尔开发者论坛,以了解如何正确使用它)。

最后,我在西雅图的软件质量初创公司Corensic工作。我们有一个名为Jinx的工具,旨在检测您的代码中的并发错误。Windows和Linux都提供了30天的试用版,因此您可能需要查看一下。(www.corensic.com)

简而言之,Jinx是一个非常轻巧的超级管理程序,激活后,它会滑入处理器和操作系统之间。然后,Jinx智能地获取执行片段并运行各种线程时间的模拟来查找错误。当我们发现一个特定的线程时间将导致出现错误时,我们会在您的计算机上使该时间成为“现实”(例如,如果您正在使用Visual Studio,则调试器将在该点停止)。然后,我们指出了引起错误的代码区域。Jinx没有误报。当它检测到错误时,那就绝对是个错误。

Jinx适用于Linux和Windows,以及本地和托管代码。它是语言和应用程序平台不可知的,可以与您现有的所有工具一起使用。

如果您查看它,请向我们发送反馈,告诉我们哪些有效,哪些无效。我们已经在一些大型开源项目上运行Jinx,并已经看到Jinx可以比简单的压力测试代码更快地找到50-100倍的漏洞情况。


3
任何高性能应用程序(使用C或C ++编写,并设计为有效利用多个处理器/核心)的瓶颈是内存系统(缓存和RAM)。单个核心通常通过其读取和写入使内存系统饱和,因此很容易看出为什么添加额外的核心和线程会导致应用程序运行更慢。如果一个人的队列可以一次通过一个门,那么添加额外的队列不仅会堵塞门,而且还会使任何一个人通过门的通道不那么高效。

任何多核应用程序的关键是优化和节约内存访问。这意味着将数据和代码结构化,尽可能在它们自己的缓存中工作,这样它们就不会通过对公共缓存(L3)或RAM的访问来干扰其他核心。偶尔,核心需要冒险进入那里,但诀窍是尽可能减少这些情况。特别是,数据需要围绕缓存行及其大小(目前为64字节)进行结构化和适应,并且代码需要紧凑而且不要跳来跳去,这也会干扰流水线。

我的经验是,高效的解决方案对于所涉及的应用程序是独特的。通用准则(如上所述)是构建代码的基础,但由于优化工作中的削减更改对于那些没有参与优化工作的人来说并不明显。


1

查找fork/join框架和work-stealing运行时。这两个名称指的是相同或至少相关的方法,即将大型任务递归地细分为轻量级单元,以利用所有可用的并行性,而无需事先知道有多少并行性。其想法是它应该在单处理器上以串行速度运行,但在多个核心上获得线性加速。

如果你从正确的角度看,这是一种水平类比于缓存无关算法。

但我认为面对多核编程的主要问题是绝大多数计算仍然顽固地串行。没有办法将多个核心投入到这些计算中并使它们粘合。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接