什么是代码现代化?

现代高性能计算机由下列资源组合构建而成:多核处理器众核处理器、大型高速缓存,高带宽进程间通讯结构和高速 I/O 功能。 高性能软件需通过设计,以充分利用这些丰富的资源。 不管是从新构建并/或调优现有应用以发挥最高性能,或为现有或将来设备构建新应用,了解编程模型和高效利用资源之间的相互做用极其关键。 以此为起点,全面了解代码现代化。 关于性能,您的代码相当重要!编程

构建软件的并行版本可以使应用在更短的时间内运行指定的数据集,在固定时间内运行多个数据集,或运行非优化软件禁止运行的大型数据集。 并行化的成功一般经过测量并行版本的加速(相对于串行版本)来进行量化。 除了上述比较以外,将并行版本加速与可能加速的上限进行比较也十分有用。 经过阿姆达尔定律和古斯塔夫森定律能够解决这一问题。数组

出色的代码设计将几个不一样层面的并行化均考虑在内。缓存

  • 第一层并行化是矢量并行化(代码内),在大型数据块上执行相同的计算指令。 代码的标量部分和并行部分都将受益于高效的矢量计算。
  • 第二层并行化是线程并行化,其主要特色是单个进程的多条合做线程经过共享内存进行通讯,共同处理某项指定任务。
  • 第三层并行化是以独立合做进程的方式开发多个代码时,各代码之间经过消息传递系统进行通讯。 这种被称为分布式内存队列并行化,之因此如此命名,是由于每一个进程指定一个独有的队列号。

开发可以高效使用这三层并行化,并具有高性能的代码是实现代码现代化的最佳选择。性能优化

综合考虑这几点会对设备的内存模式产生积极的影响:主内存容量与速度、与内存位置相关的内存访问时间、高速缓存容量与数量,以及内存一致性要求。网络

矢量并行化时,若是出现数据不对齐,会严重影响性能。 数据应以高速缓存友好型方式进行整理。 若是不这样,当应用请求不在高速缓存内的数据时,性能将会降低。 当所需的数据在高速缓存内,内存访问速度会达到最快。 高速缓存之间的数据传输均以高速缓存行进行,所以,若是下一组数据不在当前高速缓存行内,或分散于多个高速缓存行,应用的高速缓存效率会下降。数据结构

除法和超越数学函数很是昂贵,即便指令集直接支持这些函数。 若是您的应用在运行时代码内使用多项除法和平方根运算,由于硬件内的功能单元有限,性能会有所下降;链接这些单元的管道可能会占主导。 因为这些指令很是昂贵,开发人员但愿高速缓存使用频率较高的值,以提高性能。多线程

“一刀切”的技术不存在。 人们太过于依赖正在处理的某个问题和对代码的长期要求,但优秀的开发人员会关注不一样层面的优化,不只知足当前须要,还会知足将来需求。架构

英特尔构建了一套完整的工具来协助代码现代化,包括编译器、资源库、调试器、性能分析器,并行优化工具等等。 此外,做为并行计算机开发领域的领导者,英特尔以其超过三十年的丰富经验为基础提供网络研讨会、文档、培训示例,以及最佳方法和案例研究。app

面向多层并行的代码现代化 5 阶段框架

代码现代化优化框架以系统化方式进行应用性能优化。 该框架将应用分为 5 个优化阶段,各阶段相互做用,相互影响,以共同提高应用性能。 可是,启动优化流程以前,您应考虑应用是否须要从新构建(根据如下指南)以实现最高性能,而后按照代码现代化优化框架进行优化。

借助该优化框架,应用可在英特尔® 架构上实现最高性能。 这种分布式方法有助于开发人员在最短的时间内实现最高的应用性能。 换句话说,它支持程序在执行环境中最大限度地使用全部的并行硬件资源。 这 5 个阶段分别为:

  1. 利用优化工具和库:使用英特尔® VTune™ Amplifier 分析工做负载,以肯定热点,并使用英特尔® Advisor XE识别矢量化和线程化机会。 使用英特尔编译器生成最佳代码,并在适当的状况下运用英特尔® 数学核心函数库英特尔® TBB 和 OpenMP* 等优化的资源库。
  2. 标量串行优化:保持正确的精度,输入常量,并使用合适的函数和精度标记。
  3. 矢量化:利用 SIMD 特性以及数据布局优化。采用高速缓存对齐的数据结构,将结构数组转化为数组结构,并最大限度地减小条件逻辑。
  4. 线程并行化:分析线程扩展,并将线程与内核关联。 扩展问题一般是因为线程同步或内存利用率低下所形成的。
  5. 将应用从多核扩展到众核(分布式内存队列并行化):扩展对高度并行化应用来讲极为重要。 在将执行对象从一种偏心的英特尔架构(英特尔® 至强™ 处理器)换至另外一种(英特尔® 至强融核™ 协处理器)的过程当中,最大限度地减小变化并最大限度地加强性能。

5 Stages of code modernization

代码现代化 – 5 阶段的实际运用

第 1 阶段
在开始优化项目时,您须要选择一个优化开发环境。 该选择对于后续步骤具备重要的影响。 它不只会影响您获得的结果,还能大幅减小您的工做量。正确的优化开发环境能够为您提供出色的编译器工具、现成的优化库、调试工具和性能评测工具,帮助您准确地查看代码在运行时正在作什么。 查看英特尔® Advisor XE工具中的网络研讨会,并以此识别矢量化和线程化机会。

第 2 阶段
用尽了可供使用的优化解决方案后,若是还要发挥应用的更高性能,您须要启动与应用的源代码相关的优化流程。 在开展活动并行编程以前,您须要确保应用在进行向量化和并行化处理以前可提供正确的结果。 一样重要的是,您须要确保应用可以以最少的运算获得正确的结果。 您要考虑数据和算法相关的问题,如:

  • 选择合适的浮点精度
  • 选择合适的估算法准确度:多项式或有理数
  • 避免跳跃算法
  • 利用迭代计算缩短循环运算长度
  • 避免或最大程度减小算法中的条件分支
  • 避免重复计算,使用以前的结果

您还必须处理语言相关的性能问题。 若是您使用的是 C/C++,与该语言相关的问题包括:

  • 对全部常量使用外显式型态法 (explicit typing),以免自动升级
  • 选择正确的 C 运行时函数类,好比 doubles 或 floats: exp() 与 expf()abs() 与 fabs()
  • 以显性方式将点别名告知编译器
  • 显式调用内联函数,以免开销

第 3 阶段
尝试矢量级并行化。 首先尝试对内层循环进行矢量化。 为了获取高效的矢量循环,请确保控制流分散达到最少,以及内存访问保持一致。 外层循环矢量化是一种用于加强性能的技术。 默认状况下,编译器会对嵌套循环结构中最内层的循环进行矢量化处理。 但在某些状况下,最内层循环中的迭代数量较小。 此时,对最内层循环进行矢量化有些得不偿失。 可是,若是外层循环中具备更多的工做,则可使用一个基本函数组合(strip-mining 和编译指示/指令 SIMD)在外层循环强制执行矢量化操做,以实现更好的效果。

  1. SIMD 在“封包”和对齐的输入数据上表现最为出色,但因为其自己的性质,它会对控制分散形成不利影响。 此外,若是应用实施专一于数据邻近度,现代硬件会实现出色的 SIMD 和线程性能。
  2. 若是内层循环没有足够的工做(例如,运行次数很是低;矢量化的性能优点能够测量),或数据依赖性妨碍针对内层循环的矢量化,请尝试对外层循环进行矢量化。 外层循环可能会产生控制流分散;尤为是在内层循环的运行次数因为外层循环每一个迭代的不一样而有差别的状况下。 这样会限制经过矢量化而实现的性能改进。 外层循环的内存访问可能与内层循环不一样。 这样会形成收集/分散指令(而非矢量加载和存储),从而大大限制经过矢量化而实现的扩展。 数据转换(好比转置二维数组)可缓解这些问题,或尝试将结构数组转化为数组结构
  3. 因为循环层级较浅,上述指南可能会形成须要同时对循环进行并行化和矢量化处理。 在这种状况下,该循环不只须要提供足够的并行工做以弥补开销,还要维持控制流均匀性和内存访问一致性。
  4. 更多详情请查看矢量化要素

第 4 阶段
如今咱们要进行线程级并行化处理。 肯定最外层,并尝试对该层进行并行化处理。 显然,这要求维护潜在数据竞跑,并在须要时将数据声明移到循环内部。 它还要求以高效利用高速缓存的方式维护数据,以下降跨多条并行路径进行数据维护所产生的开销。 之因此对最外层进行并行化处理,是但愿为每条独立线程提供尽量多的工做。 阿姆达尔定律代表: 使用多台处理器的程序在并行计算过程所实现的加速受制于程序顺序片断所需的时间。 因为工做量须要用来弥补并行化所产生的开销,所以有利于每条线程拥有尽量多的并行工做。 若是因为不可避免的数据依赖性致使最外层没法实现并行化,请尝试对可以正确实现并行化的下一个最外层进行并行化处理。

  1. 若是最外层的并行工做量可以知足目标硬件的须要,并能随并行资源的合理增长而进行扩展,那么您已达到并行化处理的目标。 请勿进行其余并行化处理,由于这样会显著增长开销(线程控制开销将否认一切性能改进),也没法实现任何性能提高。
  2. 若是并行工做仍然不够,例如,通过内核扩展测试,最多只能扩展到少许内核,而没法扩展到实际内核数,请尝试对其余层(尽可能为最外层)进行并行化处理。 请注意,您无需将循环层级扩展到全部可用内核,由于可能有其余循环层级处于并行执行之中。
  3. 若是没法在第 2 阶段生成可扩展代码,缘由多是算法中的并行工做不够。 这表示,划分多条线程之间的固定工做量使每条线程获得的工做量极少,所以启动和终止线程所产生的开销抵消了有用工做。 也许算法可以进行扩展以处理更多工做,例如,尝试处理更大的问题。
  4. 请确保您的并行算法可以高效利用高速缓存。 若是不是,请将该其从新设计成高速缓存高效型算法,由于这种算法不随并行化而扩展。
  5. 更多详情敬请查看英特尔多线程应用开发指南系列。

第 5 阶段
最后咱们要作的是多节点(队列)并行化。 许多开发人员认为,消息传递接口 (MPI) 就是“仅运行”于场景背后的黑匣子,将数据从一项 MPI 任务传输到另外一项。 对于开发人员来讲,MPI 的魅力在于其算法编码独立于硬件。 而开发人员担心的是,因为众核架构采用 60 多个内核,任务之间的通讯可能会在单个节点内部或跨节点产生通讯风暴。 为了缓解这些通讯瓶颈,应用可采用混合技术,混合使用几项 MPI 任务和几条 OpenMP 线程。

通过良好优化的应用可处理矢量并行化、多线程并行化和多节点(队列)并行化。 然而,为了高效完成这些并行化,可以使用标准分布式方法,以确保兼顾到各阶段的层面。 根据个独立应用的特定需求,能够(一般)对上述阶段进行从新排序;您能够在某一阶段迭代两次以上,以实现预期性能。

根据咱们的经验,必须实施全部阶段,以确保应用不只可以在目前的可扩展硬件上实现出色性能,还可在将来硬件上进行有效扩展。

试试看!

相关文章
相关标签/搜索