声称自己不“多核”友好的程序


17

您会时不时看到这个短语或类似词,通常指的是声称它们并非旨在充分利用多核处理器的程序。这在视频游戏编程中尤其常见。(当然,许多程序没有并发并且不需要它,例如基本脚本等)。

怎么会这样?许多程序(尤其是游戏)固有地使用并发性,并且由于OS负责CPU上的任务调度,那么这些程序是否固有地没有利用可用的多个内核?在这种情况下,“利用多核”意味着什么?这些开发人员实际上是在禁止OS任务调度并强制进行亲和力还是自己进行调度?(听起来像是主要的稳定性问题)。

我是Java程序员,所以也许由于抽象或其他原因我不必处理这个问题。


11
一个很大的可能性是在同步中采用了快捷方式,该快捷方式适用于单处理器/核心系统,但与多个处理器/核心的真正并发性冲突。
Bart van Ingen Schenau 2014年

@BartvanIngenSchenau:这是正确的。您应该对此进行扩展并将其发布为答案。我认为所有其他人都没有讲到这一点。
凯文·克莱恩

1
我认为@Bart真的很近。但是,s / work / 似乎可以工作,并且它会更接近标记。
Ben Voigt 2014年

顺便说一句-我曾经以用户而非程序员的身份经历过-Windows XP上的Ground Control 2。我需要将核心亲和力设置为多核系统上的一个核心才能正常运行,否则所有动画(影响整个游戏)都将以10倍的速度运行,虽然更具挑战性,但一段时间后确实有点烦人。我没有在游戏上做任何工作,但是在我看来,游戏的某些部分似乎只依赖处理器同时进行一定数量的工作。
jammypeach 2014年

Answers:


28

良好的并发性不仅需要在应用程序中抛出几个线程并希望达到最佳状态,还需要更多。从令人尴尬的并行到纯顺序,程序并发的方式有很多。任何给定的程序都可以使用阿姆达尔定律来表达问题或算法的可扩展性。令人尴尬的并行应用程序的几个条件是:

  • 没有共享状态,每个函数仅取决于传入的参数
  • 无法访问物理设备(图形卡,硬盘驱动器等)

还有其他条件,但仅凭这两个条件,我们就能理解为什么特别是游戏不像您想的那样利用多核优势那么容易。首先,将要共享的世界模型必须共享,因为不同的功能可以计算物理,运动,应用人工智能等。其次,该游戏模型的每一帧都必须使用图形卡在屏幕上进行渲染。

公平地说,许多游戏制造商都使用第三方生产的游戏引擎。花费了一段时间,但是这些第三方游戏引擎现在比以前更加并行。

在处理有效的并发方面存在更大的架构挑战

并发可以采用多种形式,从后台运行任务到对并发的完整体系结构支持。某些语言为您提供了非常强大的并发功能,例如ERLANG,但它要求您对构建应用程序的方式进行不同的思考。

并非每个程序都真正需要完整的多核支持的复杂性。这样的示例之一是税务软件或任何表单驱动的应用程序。当您大部分时间都花在等待用户做某事上时,多线程应用程序的复杂性就没那么有用了。

一些应用程序使自己可以使用更尴尬的并行解决方案,例如Web应用程序。在这种情况下,平台以令人尴尬的方式并行启动,这取决于您不必强加线程争用。

底线:

并非所有应用程序都没有充分利用多个线程(因此没有内核)而受到真正的伤害。对于那些受此影响的应用程序,有时计算对并行处理不友好,或者协调起来的开销会使应用程序更加脆弱。不幸的是,并行处理仍然不如要做的好。


这是一个很好的分析。不过,令我感到困扰的一件事是,您对现实世界中的程序通常不会尴尬地并行化,因此很难并行化:尽管并行做同一件事可能是不可能的,但是并行进行不同的事情可能很容易(例如在管道架构中,或在单独的UI线程中)。
阿蒙2014年

8
真正的重点是您需要设计用于并行执行,否则,您将因缺乏设计而受到限制。我同意,它可以很容易在并行做不同的事情,但如果它的高用户期望现有的应用程序。在那种情况下,它很可能需要重写才能实现。重写具有固有的风险,但有时您可以为其辩解。我做了两次这样的重写,它们在最大程度地提高并行处理能力的同时保留了尽可能多的代码。有很多隐藏的因素。
Berin Loritsch 2014年

好答案。值得强调的是,在并行化某些系统时,不仅收益会递减,而且由于使它们并行化所需的开销,实际上某些系统的速度可能会变慢。特别是,许多信号灯/锁和上下文切换可能会对运行时产生不利影响。特别是上下文切换可能会降低缓存的效率,如果您要优化系统,这将是一个不小的问题。OP尤其以游戏引擎为例,使我回想起除了并行访问外,还有更多关于优化缓存的知识。
Gankro 2014年

35

许多程序(尤其是游戏)固有地使用并发性,

不,实际上是相反的。大多数应用都是以单线程思维方式编写的,开发人员从未进行必要的更改来支持并发。

在C,C ++和C#中,您需要明确告知应用程序启动新线程和/或进程。

我认为您过多地关注线程的调度,而没有过多关注潜在线程中的数据处理。跨线程和/或进程共享数据需要某种形式的同步。如果您将应用程序更改为使用多个线程,但未能实现该同步,则可能会发现很多难以跟踪代码中的错误的地方。

对于我研究过的多线程应用程序,我通常从不担心调度,而只担心数据同步。我唯一需要担心的问题是,由于数据同步不正确,我在追逐比赛条件时。

通常,当应用程序说它不能使用多个内核时,这意味着它们没有适当的同步来保护数据操作。


即使对于大型开发者/发行者的新现代程序来说,也是如此吗?当我坐下来编写程序时,我想到的设计阶段的第一件事就是,我需要并发吗?因为这可能导致完全不同的设计。特别是游戏必须具有一定程度的并发性,否则当数千种屏幕模型之一尝试执行某项操作时,游戏将冻结。
SnakeDoc

5
@SnakeDoc-我认为您在那儿混淆了您的域名。大型游戏公司肯定会考虑并发性,但是我还没有看到大型游戏公司不支持并发性。我见过的不支持并发的应用程序和游戏通常来自较小的商店/个人开发人员,而他们本来不会以这种思维方式开始。并且在应用程序发展的某个时刻,事后不可能进行并发。而且某些应用程序从未打算做足够多的工作来证明并发。

而且,某些游戏也可以依靠新内容(图形和游戏玩法)而繁荣发展,而不必更新游戏引擎(代码实现)。因此,游戏引擎在技术上可能落后数年。
rwong 2014年

6
@SnakeDoc:您不需要并发来处理成千上万的屏幕模型。并不是游戏中的每个对象都需要自己的线程来模拟它;一个线程可以处理每个时间步上屏幕上所有内容的更新。
user2357112支持Monica 2014年

13

这与多核无关,而与多线程有关。操作系统可以调度线程在其喜欢的任何内核上运行,并且这种调度对于正在调度的程序是透明的。但是,许多程序不是使用多个线程编写的,因此它们只能一次在一个内核上运行。

为什么要编写单线程程序?它们更易于编写和调试:一种事情接连发生(而不是同时发生并可能互相影响的多种情况)。否则您的程序可能没有针对多核计算机(就像旧游戏一样)。在某些情况下,如果上下文切换和线程之间的通信所产生的开销超过了并行执行所获得的速度,则多线程程序甚至可能比单线程版本运行得慢(程序的某些部分可能无法并行化)。


8

这不是一个完整的答案。这是一个警告性的故事。

有一天我以为我会向我的并发编程课程中的学生展示一个并行的快速排序。我认为Quicksort应该很好地并行化。我使用了两个线程。在我的单核计算机上运行它。结果是:

  • 单线程版本为14秒。
  • 2线程版本为15秒。

这是我所期望的。

然后,我在较新的双核计算机上进行了尝试。

  • 单线程版本为11秒。
  • 2线程版本为20秒。

这两个线程共享剩余任务的队列。似乎队列对象的字段在一个内核的缓存和另一个内核的缓存之间来回移动。


2
您测试了多少个数组元素?也许mergesort更合适,因为多核编程需要复制数据以避免高速缓存行冲突?
rwong 2014年

2
@rwong有10,000,000个数组元素。当然,mergesort可以很好地并行化。如果我使用合并排序,那么我可能不会学到有用的课程。
Theodore Norvell

1
@ArlaudPierre我将考虑并行化任何算法。Quicksort很有趣,因为您可以使用任务袋方法。由于任务是独立的,因此我的直觉是它应该是令人尴尬的并行性的一个示例。我要指出,有些调整之后,它实际上有接近2的加速
西奥多·诺维尔

1
@Jules答案是负载平衡。我也想以一种易于更改线程数的方式编写它。您的方法可以很好地推广到2的幂,但是对于其他数量的线程则不太好。
Theodore Norvell

2
@MaciejPiechotka道德几乎就是您所建议的所有事情。但是回到OP,我认为最相关的道理是,除非花费了很多精力来确保,否则多线程程序在多核体系结构上的运行实际上可能比在单核处理器上运行的慢得多。
Theodore Norvell 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.