将软件推向未来多核架构
通过增加主流处理器的执行内核提高硬件并行处理能力——这一业界变革要求软件制作人员做出相应的转变。关键的一点便是要求他们着眼于未来可用的硬件资源,并提前为这些资源做出合适的架构选择。本文将继续就上述开发规划进行探讨。
作者:Matt Gillespie
向多核处理的转变已彻底颠覆了计算行业长期以来的一个信条,即要提高软件性能就需要开发新一代硬件。以前,人们的期望大多寄托在日渐提高的时钟速度上,这是整个计算史上处理器演进的一大特点,然而最近这种情况有了改变。
功耗和散热问题引起了越来越多的关注,它使原本可预见的处理器工程的未来离开了提高处理器速度的轨道,而转向通过增加每块芯片上执行内核的数量来提高并行处理能力。处理器时钟速度的提高几乎没有为软件基本设计带来什么启示(除了诸如随性能增加可添加更多特性的能力外)。不过,上述新趋势对软件的灵活性提出了更高的要求,同时也意味着程序员要有更强的预见能力。支持这种扩展将成为软件实现性能和可扩充性的基本要求,也直接昭示着其市场竞争力。
多核处理如今已无处不在,从笔记本电脑到企业服务器等,应用范围极其广泛。它造成软件行业普遍开始在产品架构方面进行某种程度(通常是适当程度)的变革。现在,很多应用程序都经过了某种程度的线程处理,一般扩充至两颗、四颗或八颗虚拟处理器。这种级别的线程处理尽管很复杂,却较能接受不太理想的线程方法。
随着多核架构的持续演进,开发人员需要把线程技术作为解决方案的核心(而不仅仅是一项优良的特性)进行优化,而与线程制作、同步和锁定、线程粒度、调度和进程管理等相关的费用问题将随着时间的推移日益突出,且对并行可扩充性的规划将会变得越发重要。
为便于论述,在此有必要说明“多内核”一词在业内常见的两种不同用法。第一种用法是指包含大量执行内核的处理器架构;此类架构还有另一种不太常用的名称“大量多核”。第二种用法是指基于异构内核、具有图形处理或 TCP/IP 卸载等专用功能的处理器。本文采用的是“多内核”的第二种用法,而“大量多核”在此则指代含有同构 CPU 类内核的大型系统。
本文对一些基本的技术问题和与软件开发机构的多核未来规划相关的行业趋势进行了调查,旨在为决策者、有影响力的人和软件架构师提供一些指导,帮助他们制定恰当的方针和战略,进而在多核时代来临之际立于不败之地。
如欲获得本材料的执行摘要,请访问多核开发人员社区 查看技术简介。
对于内核数量日益增多的系统,为其开发编程策略最重要的建议可能就是使可靠的线程实践走向制度化。可靠的线程实践不仅可带来解决方案性能的短期提升,还能随着所需线程数量的增加,在未来优化上述解决方案的线程设计。
实施通用线程实践应包括一种结构化方法,这种方法充分利用可简化该流程的多种工具和库。英特尔® 软件网络多核开发人员社区 提供了适合开发综合线程方法的多种资源。以下此类流程的超简化版本阐释了生命周期内的重要任务:
构建应用程序的串行版本,以确定用于并行处理的候选区域。该任务可通过以下方式加速完成:利用英特尔® VTune™ 性能分析器或 Microsoft Visual Studio 2005* 采样调节器等工具来对应用程序进行取样,识别应用程序内占据处理器大部分时间的热点。然后,使用英特尔VTune 性能分析器的调用图功能识别关键路径(见图 1 红色部分),选择最耗时的(或可并行的)路径。观察调用顺序和有助于识别线程调用的合适函数来检查选定的路径。通过此阶段的分析,可以估算出通过对讨论部分进行并行处理可能获得的速度提升,进而树立性能提升目标。
图 1 英特尔® VTune™ 性能分析器调用图输出
设计合适的线程模式以对候选区进行线程处理。这一步包括将任务视为数据分解(或函数分解)和重构数据的候选操作以适应线程处理。简而言之,数据分解是将同样的逻辑应用于不同的数据集,正如将一个过滤器应用于一副图形图像,在该过程中,同种算法可同时(并行)应用于图像的不同区域。函数分解是将整体逻辑分成多个独立的函数任务,如同时进行升级 UI 窗口、执行数据查询和计算公式等任务。此外,这一步还包括确定线程安全库函数(如英特尔® 集成数据基元和英特尔® 数学核心函数库中的函数)涉及的算法的值。
实施线程处理。执行设计阶段确定的库函数,为即将进行线程处理的应用程序赋值。对于函数分解问题,使用显式线程,并在启动时创建一个线程池,将一个或更多任务函数进行压缩,在不活跃线程间分配任务。对于数据分解问题,使用 OpenMP* 可显著简化实施过程,这是因为实施细节大都由 OpenMP 运行时处理。
调试和测试线程代码。一旦实施了选定的线程模式,且代码已借助编译器或性能分析器得到了插装,英特尔® 线程检查器将会帮助循环识别和消除线程错误。务必确保代码生成的结果与串行版本一致。本步骤包括对出现内存冲突的模块实施源代码插装、以及使用英特尔 线程检查器深入检查与模块相关的源代码行。
优化代码以实现性能目标。收集了性能统计数据后,将英特尔VTune 性能分析器和英特尔线程调节器结合使用来检测线程不平衡的情况。各个击破线程不平衡问题,并重新检查应用性能,确定是否已达到调节目标;如有必要,重复此流程。
应根据各机构的需要和偏好开发一个结构化方法,并根据需要对其进行维护和优化。
对代码的某个特定区域进行线程处理所带来的可用加速部分取决于该区域生成的线程数量。一般情况下,线程数量最好不要超过可用处理器资源能够同时处理的线程数。因此,对于一个支持超线程技术且含有两颗处理器的四核系统,理想的最大线程数为 16(2 颗处理器 x 每颗处理器 4 枚内核 x 每枚内核 2 颗虚拟处理器)。这一点至关重要,因为,如果构建的线程数超出执行硬件的实际需要,那么多余的线程将不会提供性能优势,反而带来更多额外的开销。
一部分代码可以在各种大小的系统上执行,包括采用某些处理器(在软件之后推出且具有更多可用内核)的系统。因此,在不同级别的硬件资源条件下,应用代码能够适应性地创建各种数量的线程十分重要。理想状态下,这种适应应对用户公开,尽管这取决于应用类型,但技术用户(如服务器应用的系统管理员)最好能够对此行为拥有某种程度的控制权。
满足上述要求的方法之一便是,使用一种类似 OpenMP* 的线程模式,在运行时自动创建最佳数量的线程,为运行软件的机器提供支持。另一种方法是使用 CPUID 指令,计算处理器封装中内核和逻辑处理器的数量。如欲了解有关此方法(包括用于实施的实用程序)的有益探讨,请查阅《检测 IA-32 平台中的多核处理器拓扑》 。
为确保代码生成的线程数量不超出最大数量(为此已对代码进行了测试),开发人员有时会采用一些机制(如 OpenMP 函数 omp_set_num_threads() )为代码生成的线程数量设定上限,人为地限制线程数量。尽管此举可确保您的应用正常运行且性能无损,但它同时也限制了自身对未来硬件架构的支持。但幸运的是,目前有很多方法均可解决这一问题。
首先,最好在支持大量线程的硬件系统上测试软件的可扩充性。在理想状态下,应在预生产阶段寻找进行此类测试的硬件,同时需考虑英特尔® 软件合作伙伴计划 等站点提供的辅助开发的技术资源(包括访问开发平台和工程支持)。该计划还为管理人员和决策者提供规划和战略资源,并为企业提供市场推广支持。
另一种方法是为应用创建的可配置线程设置数量上限。此方法允许您在含有大量内核的可用硬件上轻松测试软件,如果您的机构通过验证认可了软件的性能,您便可根据需要更改应用设置获得更多线程。
由于每颗处理器都有大量的可用内核,系统可靠性的责任将从个别内核转移到总体内核上。换言之,大型内核系统能够容许个别单元出错,因为每枚内核对应的总体执行资源相对较小。传统上,新组件的老化是识别那些在生命周期中较早衰退组件的一种重要方法,也是分离用于生产的“长寿”单元的一种手段。与只拥有数枚内核的系统不同,对一个含有几千枚内核的系统采用剪切的方法在一个大量多核的未来显得有些不切实际。
此外,如果假定处理器组件的平均故障间隔时间为十年,则我们可以粗略地推断可能会有一半的组件在系统使用的头十年内出现故障。对于一个含有 1,000 枚内核的系统而言,这将意味着平均每周会有大约一枚内核无法使用。尽管如此,系统仍然有大量的处理能力来维持运转,但软件必须能够动态地适应这个环境,这反过来又会使基于不可靠个体组件的系统保持高度的弹性。
此模式下必要的检错操作可能取自现有的高性能计算模式,在高性能计算模式下,电脑的分配式系统通过绝对数量而不是机器内部的资源实现冗余。另一个类比是互联网上的 TCP 流量;在互联网上,发生故障的节点或传输路径可在不致使处理流程失败的情况下得到传递。
许多观察人士预计,专为支持特定任务而设计的、含有大量内核的处理器将在今后几年内越来越流行。片上异构允许处理器根据每项应用的需要,更好地匹配执行资源,并高效解决更广泛的系统负载问题。
例如,与传统 CPU 内核相比,专用显卡或 TCP/IP 卸载内核在执行某些功能时可能具有更高的功效;但同时,CPU 内核在部署到合适的环境中时也具有更高的灵活性。由于现有技术揭示了这一领域的未来发展趋势,可以考虑将图形处理单元 (GPU) 用于非图形目的以及在多核架构中囊括可编程门阵列 (PGA)。
现在,越来越多的研究和实践团体均以用作通用协处理器为工作重点。这一领域被称为通用计算图形处理单元 (GPGPU),尽管该领域并未得到广泛的开发利用,但却展示出十分光明的前景,并澄清了与使用软件来充分利用一个处理器封装内多种执行内核相关的某些概念。总体而言,图形处理任务本质上具有高度并行性。渲染和代码转换等任务为将工作负载划分为独立任务提供了一种非常显见的方法,可使相同的操作同时得到执行(如在静态图形内的多个像素区域或一个动画内的多个时间片中)。
因此,GPU 架构在本质上具有高度并行性,这使其能够妥当高效地处理多种操作。例如,矩阵代数可以轻松地直接映射至 GPU 内渲染操作所用的 2D 网格中,因此需要大量快速傅立叶转换来实施的矩阵操作(如金融市场分析)是涉及流体动力学、医疗成像和地质勘察等领域的问题,也自然是此模型的理想之选。
在利用 GPU 编程实现通用计算的发展过程中,一项重大进步是 Nvidia 发布的 CUDA*,这是一个 C 语言编程环境,专门用于依靠 GPU 解决复杂的计算问题。CUDA 环境与 Windows* 和 Linux* 编程环境兼容,且包括数学函数库和一套可辅助实施的 SDK 。目前,其他厂商已开始推出面向 GPU 的编程资源,这标志着该领域已走向成熟,在未来有望直接支持面向多核架构的软件开发。
在其固有灵活性的基础上,可编程门阵列 (PGA) 成为未来多核处理器能够提供的另一种功能。PGA 通常能够提供可编程逻辑块,能够通过重新配置提供各种功能。尽管某些设施在运行时存在动态配置,但它们通常针对特定用途而配置,并在静态方式下使用。通过扩展这些动态性能,我们就可以想象处理器能够检测到特定工作负载的要求,并动态地重新配置实际硬件来满足其要求(例如在能效与性能之间达到完美平衡)。
硬件行业的变化过程是显而易见的——在可预见的未来,处理器演进的主要途径是不断增加每个处理器封装内的执行内核数量。这种变化要求软件的并行能力应不断提高,尤其是通过多线程来实现。很多开发机构所做出的将对他们的软件进行线程处理的有限承诺必须被强有力的线程方法所取代,该方法将应用于他们所有的产品中,确保其产品能够充分发挥出下一代处理器的优势。
除采用传统的线程技术外,开发人员还应考虑采用超出现有数量的更多处理内核。提供越来越多的可用内核及动态适应执行环境的价值将不断提高,而不是设计一件适用于一系列处理器的产品。
随着系统日益增大,最佳的线程数量将不再取决于对所有可用内核的匹配,而是根据软件内算法设置的级别来确定。同时,含有巨量内核的处理器将向可用资源中基于动态调整的弹性系统转变,提高个体内核的容错能力。
在更遥远的未来,处理器可能会利用大量的专用异构内核,在芯片上形成分配系统。针对特定的处理内核卸载某些功能将会提高资源和任务的匹配能力并带来更高的能效。目前具备此项功能的前代处理器可能得益于将通用协处理器作为 GPU 使用以及相关的实施(揭示开发机构如何为未来大量多核和多内核系统的多种处理资源做好准备)。
您可围绕如下材料进一步探讨本话题:
- 英特尔® 多核开发人员社区 将与创建能够充分利用多核处理技术的软件相关的各种开发资源集中在一起。
- 英特尔® 多核技术与研究门户 ,帮助您了解当前英特尔多核技术的各种资源,以及正在进行的创新和研究。
- 《解决微处理器能量复杂性问题的多核方法》 就可减少处理器能耗的异构多核架构进行了探讨。
- GPGPU.org * 专门收集关于使用图形硬件用于通用计算的新闻和信息,包括信息板和业界信息发布等内容。
作者简介
|
Matt Gillespie 是芝加哥地区的技术文档独立作家和编辑,其专攻领域为新兴的硬件和软件技术。在此之前,Matt 在英特尔公司为软件开发人员开展培训课程,他还曾任职于加州联邦银行互联网技术服务部。在其职业生涯的最初几年 |