首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

多核多处理器架构软件设计的注意事项简介

多核多处理器架构软件设计的注意事项简介

简介计算硬件正在快速蓬勃发展。在时钟速度呈平稳状态的同时,晶体管密度在不断增长。处理器制造商希望通过让每个芯片拥有多个核和硬件线程来提高多处理能力。例如, 对称多处理器架构通过每个核支持多达 4 个线程、每个芯片支持 8 个核、每台服务器支持 32 个芯片插槽来实现高度并行性,总共 1024 个并发硬件线程。相比之下, 架构仅支持每个核 2 个线程、每个芯片 2 个核、每台服务器 32 个芯片插槽,总共 128 个并行硬件线程。
开发软件时,目前设计人员需要考虑可能会部署软件的多处理器、多核架构。这是因为:
  • 通过使用更多核、硬件线程、更高的内存,应用程序应该会更好地执行和更出色地伸缩性,并能满足不断增长的性能和效率需求。
  • 随着越来越多地使用多核、多处理器系统,软件设计注意事项现在应该考虑包含能够在这些计算资源之间有效地分发软件功能的方法。
  • 如果在设计的过程中没有考虑这些考虑事项,那么在多处理器、多核环境中运行应用程序可能会导致严重且不易发现的性能问题。
本文将会简要介绍为多核、多处理器环境设计软件时的一些重要注意事项。
            在芯片多线程、多核、多处理器架构上软件可伸缩性的阻碍应用程序应该能够在多核、多处理器环境中更好地伸缩、更出色地执行。但是,如果应用程序设计的效率低下,则可能会在这样的环境中执行得较差,但通过使用可用的计算资源可以更好地伸缩和更出色地执行。这个可伸缩性的一些重要阻碍可能是:
  • 效率低下的并行化:单片应用程序或软件无法有效使用可用的计算资源。您需要将应用程序组织成并行任务。在传统的不支持多线程的应用程序或软件中,我们会经常看到这个问题。这些应用程序在多核、多处理器、芯片多线程硬件上无法伸缩,并且无法实现更好的吞吐量。线程太多可能会和线程太少一样,都不会产生好的结果。
  • 串行瓶颈:在多个线程或进程之间共享数据结构的应用程序可能会有串行瓶颈。为了保持数据完整性,可能必须使用锁定和串行化技术(例如,读取锁、读写锁、写入锁、自旋锁、互斥等)将这些共享数据结构的访问串行化。设计得效率低下的锁可能会由于多个线程或进程之间的高度锁争用而导致串行瓶颈,从而尝试获取锁。这可能会潜在地降低应用程序或软件的性能。应用程序的性能可能会随着核心或处理器数量的增加而降低。
  • 对操作系统 (OS) 或运行时环境的过度依赖:您不能依赖操作系统、运行时环境或编译器来完成伸缩应用程序或软件所需的一切操作。但是,编译器和运行时环境可以帮助提供一定的优化,您不能依赖它们解决所有可伸缩性问题。例如,不能依赖 Java™ 虚拟机 (JVM) 通过自动并行来发现 Java 应用程序的最佳可伸缩的机会。
  • 工作负载的不平衡可能是一个瓶颈:工作负载的不均匀分布可能导致无法有效地利用计算资源。您可能必须将较大的任务划分成可以并行运行的较小的任务,还可能必须将串行算法更改为并行算法,以便提高性能和可伸缩性。
  • I/O 瓶颈:由于阻止磁盘输入/输出 (I/O) 或高网络延迟而导致的瓶颈可能会严重抑制应用程序的可伸缩性。
  • 无效的内存管理:在多核平台上,因为有很多处理单元,因此纯计算可能非常廉价,并且主要内存可能也不是问题,因为它正在变得越来越大。但是,内存带宽一直是一个瓶颈,因为所有处理器核心都贡献了一个通用的总线。无效的内存管理可能导致一些难以检测到的性能问题,比如伪共享。
低处理器利用率可能会明确表明资源利用率未达到最佳值。为了了解性能问题,您需要评估一下应用程序是否具有太少或太多的线程,是否有锁定或同步问题、网络或 I/O 延迟、内存抖动或其他内存管理问题。只要资源是花费在有意义的工作的应用程序线程上,高处理器利用率通常都会不错。
            芯片多线程 (CMT)、多核和多处理器 (MP) 系统概述在讨论芯片多线程、多核、多处理器环境的设计注意事项之前,我们会简要介绍这类系统。图 1 所述的系统有两个处理器,每个处理器有两个核心,并且每个核心有两个硬件线程。每个核心有一个 L1 缓存和一个 L2 缓存。因此,每个核心可能都拥有自己的 L2 缓存,或者同一个处理器上的核心可能会共享 L2 缓存。同一个核上的硬件线程会共享 L1 和 L2 缓存。
图 1. 一个典型的芯片多线程、多核、多处理器系统所有核和处理器都共享系统总线,并通过系统总线访问主要内存或 RAM。对于应用程序和操作系统,该系统看起来就像是 8 个逻辑处理器。
以下重要概念将帮助我们了解为这样一个芯片多线程、多核、多处理器环境设计应用程序时所面临的挑战。
缓存一致性缓存一致性是表示处理器缓存中的数据项目值与系统内存中的数据项目值相同的一种状态。该状态对于软件来说是透明的。但是,系统为了保持缓存一致性而执行的操作可能会影响软件的性能。
考虑这样一个示例:我们假定在图 1 所述的系统中,线程 1 正在处理器 0 上运行,而线程 2 正在处理器 1 上运行。如果这两个线程都正在读取和写入相同的数据项,那么该系统必须执行额外的操作,以确保在进行每个读取和写入操作时,线程都看到相同的数据值。
当线程 1 在与线程 2 共享的数据项中执行写入操作时,会在其处理器缓存和系统内存中更新该数据项,但不会在线程 2 处理器的缓存中立即更新数据,因为线程 2 可能不再需要访问该数据项。如果线程 2 之后访问该数据项,则其处理器上的缓存子系统必须首先从系统内存中获取新的数据值。因此,线程 1 的写入会迫使线程 2 在下次访问该数据时等待从系统内存中读取数据。仅当数据被其中一个线程修改时,才会出现这种情况。如果每个线程都进行了一系列的写入操作,那么这可能会严重影响系统的性能,因为所有时间都花费在等待更新系统内存中的数据值上。这种情况被称为“乒乓效应”,当在多处理器和多核系统上运行时,应避免发生这种情况,这是一项重要的软件设计注意事项。
嗅探 - Snooping这是一个跟踪每个缓存行的状态的缓存子系统。该系统使用一个称为 “总线动态监视” 或者称为“总线嗅探” 的技术来监视在系统总线上发生的所有事务,以检测缓存中的某个地址上何时发生了读取或写入操作。
当这个缓存子系统在系统总线上检测到对缓存中加载的内存区域进行的读取操作时,它会将该缓存行的状态更改为 “shared”。如果它检测到对该地址的写入操作时,会将缓存行的状态更改为 “invalid”
该缓存子系统想知道,当该系统在监视系统总线时,系统是否在其缓存中包含数据的惟一副本。如果数据由它自己的 CPU 进行了更新,那么这个缓存子系统会将缓存行的状态从 “exclusive” 更改为 “modified”。如果该缓存子系统检测到另一个处理器对该地址的读取,它会阻止访问,更新系统内存中的数据,然后允许该处理的访问继续进行。它还允许将该缓存行的状态标记为 shared
有关这些概念的详细信息,请参阅  部分中有关 “多核多处理器系统的软件设计问题” 的文章。
多核多处理器环境对软件设计决策的影响当设计要在多核或多处理器系统上运行的软件时,主要的考虑事项是如何分配将在可用的处理器上完成的工作。分配该工作的最常用方法是使用一个线程模型,该模型可将工作分成可以在不同处理器上并行运行的各个执行单元。如果线程之间彼此完全独立,那么它们的设计无需考虑它们之间的交互方式。例如,在一个系统上运行的两个程序,每个程序都使用单独的进程在自己的核心上运行,因此无需彼此进行关注。程序的性能不会受到任何影响,除非它们争用共享的资源,比如系统内存或相同的 I/O 设备。
接下来要讨论的重点是核心和处理器与主要内存的沟通方式,以及这对软件设计决策有何影响。
请参见以下重要的设计注意事项。
避免内存争用在内存和缓存中,各种不同的核共享一个通用的数据区域,这需要在它们之间进行同步。当不同的核同时访问同一个数据区域时,会发生内存争用。在不同的核之间同步数据会因总线通信、锁定成本以及缓存缺失而有很大的性能损失。
如果应用程序有多个线程,并且所有线程都更新或修改同一个内存地址,那么正如前面部分所讨论的那样,为了保持缓存一致性,可能会产生一次重大的乒乓效应。这会导致性能降低。
有关的详细信息,请参阅  部分中 “多核平台的内存问题” 文章的 “内存争用” 部分。该文章包含一个简单的程序,此程序演示了内存争用的不良影响。该示例展示,即使多个线程之间只共享一个变量,当对更新使用原子指令时,性能损失也会很大。
避免内存争用的技巧
  • 不要在核之间共享可写入的状态:
    • 为了最大程度地减少内存总线通信,可以通过最小化共享位置/数据尽可能地减少核心交互,即使共享数据没有锁保护,而有一些硬件级别原子指令(如 Microsoft® Windows® 32 位平台上的 InterlockedExchangeAdd64)保护也是如此。
    • 减少线程之间的内存争用的一个方法是从多个线程中消除对共享内存区域的更新。例如,即便是在多个线程需要更新全局计数器或累计总数(如统计数据)时,各个线程也可以保持线程本地总数,并让全局总数仅在需要时通过一个通用的线程进行更新。因此,在共享内存区域上的争用会大大减少。
    • 趋向于减少锁争用的模式会减少内存通信,因为它是一个共享的可写入状态,该状态需要使用锁并产生争用。
  • 避免由核缓存造成的伪共享。有关的详细信息,请参阅下一小节。
返回列表