Vectorization Advisor 助您一臂之力

已发布:12/02/2015   最后更新时间:12/02/2015

Vectorization Advisor 助您一臂之力

如果您还未尝试过使用全新 Vectorization Advisor,那么强烈推荐您阅读一下这篇故事,了解它如何为客户提供巨大的帮助。它就像一位值得信赖的朋友,为您检查代码,并根据实际情况提供建议。本文还包含了有关该工具的用户反馈:“按照 advisor 的输出操作可显著提高速度,这款工具真的令我折服!”

飞机表面、喷墨打印,和分离名牌新款洗发露中使用的水油混合体或蛋白溶液,这三者之间的共同点是什么? 计算化学家会告诉您,要想推动这些领域的发展,通常需要执行凝聚态物质介观模拟。 “介观” 指此类模拟必须涉及体积稍大于原子的物体的质量,而 “凝聚态物质” 意味着您很可能要为液态或固态建模。

为满足与介观相关的各种科学工业要求,英国科学与技术设施理事会达斯伯里实验室 (STFC Daresbury Laboratory) 的研究科学家开发了一种名为 “DL_MESO” 的介观模拟程序包。 欧洲工业界的一些企业(比如 UnileverSyngentaInfineum)采用了 DL_MESO,他们使用介观模拟推导出配制洗发露、洗衣粉、农药或石油添加剂的最佳配方【计算机辅助配方 (CAF)】。

Figure 1. Visualization of the 3D_PhaseSeparation benchmark.

图 1. 3D_PhaseSeparation 基准测试虚拟化

计算机辅助配方模拟流程通常费时费力,且浪费资源。因此从一开始,达斯伯里实验室的专家就对 DL_MESO 面向现代平台的性能感知设计和优化功能非常感兴趣;这也可以解释 DL_MESO 为什么能够成为 Hartree 和英特尔开展的英特尔® 并行计算中心(英特尔® PCC)合作项目之一1。英特尔 PCC 项目致力于使用最新技术在最新系统上实现代码现代化。 就这一点而论,旨在采用新技术,为代码现代化提供帮助的 DL_MESO 自然而然地契合了英特尔 PCC 项目。

DL_MESO 工程师充分利用了 2015 年年初发布的早期预发布版 Vectorization Advisor 分析工具(产品版 Vectorization Advisor 现已成为英特尔® Parallel Studio XE 2016英特尔® Advisor 2016 的一部分。)

DL_MESO 开发人员计划充分发挥现代英特尔平台的矢量并行化功能,这进一步提高了他们使用新技术的兴趣。 就多核英特尔® 至强™ 处理器或众核英特尔® 至强融核™ 平台而言,代码只有充分利用 CPU 并行化的两个层级(多核并行化和矢量数据并行化),才能达到较高的性能。 相比于未经矢量化代码,借助 512 位宽 SIMD 指令实现高效矢量化的代码,从理论上来说,可将面向双精度浮点计算的性能提高 8 倍(或将面向单精度浮点计算的性能提高 16 倍)。 DL_MESO 开发人员不希望浪费如此高的性能。

在本文中,我们将介绍达斯伯里实验室的计算科学家 Michael Seaton 和 Luke Mason 如何使用 Vectorization Advisor 分析 DL_MESO Lattice Boltzmann Equation 代码2。 Hartree 的一位首席开发人员对使用 Vectorization Advisor 所带来的成效惊叹不已,他热情地写到:“按照 advisor 的输出操作可显著提高速度,这款工具真的令我折服!”

在全新多核英特尔至强处理器和众核英特尔至强融核协处理器上,只要确保应用充分利用了 CPU 并行化的两个层级(多核并行化和矢量数据并行化),就可以实现最佳性能。

对应用进行矢量化处理的技巧有多种,包括:

使用已经完成矢量化的库,比如英特尔® 数学核心函数库(英特尔® MKL)。 这种方法的优点是,你可以使用优化后的函数库的功能,从而省去大量进行代码矢量化所需的编程工作。

让编译器自动对代码进行矢量化处理。 使用英特尔编译器时,许多开发人员都会依靠这种传统方法 — 而且英特尔编译器表现得非常出色!

明确添加编译指示或指令,比如 OpenMP* SIMD 编译指示/指令。 开发人员越来越多地选择采用这种方法,因为这样实现的矢量化控制水平要高于仅依赖自动矢量化 — 不会受到编程水平过低的限制。

使用矢量内联函数、C++ 矢量类或汇编程序指令插入矢量感知型代码。 这种技巧要求您熟悉如何操作支持矢量化的函数和指令。 以这种方式编写的代码,其便携性大大低于采用上述方法编写的代码。

无论选择采用哪种方法生成矢量化代码,最终的代码必须能够高效执行处理器的矢量单元。 在 DL_MESO 库中,达斯伯里实验室的编程人员使用 OpenMP 4.x 编程标准来提高矢量化性能。

Vectorization Advisor

Vectorization Advisor 是英特尔® Advisor 2016 的两个主要特性之一。 英特尔 Advisor 包括 Vectorization AdvisorThreading Advisor

Vectorization Advisor 是一种分析工具,支持您:

  • 对于未矢量化循环,发现阻止代码矢量化的问题,并提供有关如何进行矢量化的提示。
  • 对于使用现代 SIMD 指令的矢量化循环,测量其能效,并提供有关如何提高能效的提示。
  • 对于矢量化循环和未矢量化循环,了解内存布局和数据结构如何能够为矢量提供更多便利。

Vectorization Advisor 可用于任何编译器,但与英特尔编译器同时使用时可以发挥最大作用。 英特尔 Advisor 不仅能够以用户友好型视图的形式显示英特尔编译器生成的各种报告,还能够以精美雅致的格式整合编译时分析结果、贡献二进制静态分析,以及 CPU 热点和精确的循环运行次数等运行时工作负载指标。

合并静态和动态分析的同时,它还会提供一些建议,供您在优化过程中使用。 Vectorization Advisor 可以弥补静态编译器时和动态运行时之间的认知空白,从而提供交互式反馈的优势和丰富的动态二进制分析文件3

英特尔 Advisor Survey:一站式 DL_MESO 性能概述

英特尔 Advisor 用户界面经过精心设计,可以将代码的所有突出矢量化特性整合至一处,形成类似一站式的服务 — 图 2 显示了使用英特尔 Advisor 的矢量化调查分析和运行次数等特性对 Lattice Boltzman 组件进行的初始分析。

英特尔 Advisor 调查报告显示,十大热点耗费了总体执行时间的一半,它们的耗时大体相同,并没有特别耗时的热点;所有耗时的循环占累计程序时间不到 12%。 这类分析具有相对平缓的特征。 平坦的分布曲线对软件开发人员来说通常是一个坏消息,因为为了实现显著的累计工作负载加速,需要查看多个热点,并对各热点进行单独分析和优化,如果没有软件工具的帮助,这项工作将十分费时。

Figure 2. Survey Report with Trip Counts.

图 2. 运行次数调查报告

Vectorization Advisor 支持对热点进行快速分类,如下所示:

  1. 要求一些最小的程序变化(大部分借助 OpenMP 4.x 完成)以支持编译器驱动型 SIMD 并行化的可矢量化、但未矢量化的循环。调查报告中的前四大热点均属于这一类型。
  2. 性能可通过简单优化技巧得以提高的矢量化循环。
  3. 性能受制于数据布局(因此要求进行代码重构以进一步加快执行速度)的矢量化循环。 稍后我们将看到,采用与上述两类循环相应的技巧后,热点 #1 和热点 #2 将变成这一类型的循环。
  4. 性能表现良好的矢量化循环。
  5. 其他(包括不可矢量化内核)。

Vectorization Advisor 不仅提供有关循环的信息,您还可以使用 RecommendationsCompiler Diagnostic Details 选项卡了解更多关于具体问题的信息,以及如何解决这些问题。

在我们的案例中,第三个热点 fGetSpeedSite 无法矢量化,因为编译器无法计算循环迭代的次数。 图 3 所示为英特尔 Advisor 针对该问题显示的 Compiler Diagnostic Details 窗口,其中还列举了示例以及解决该问题的建议。 按照给出的建议后,该循环轻松完成了矢量化,并从第 2 类循环转变成第 4 类循环。

Figure 3. Interactive Compiler Diagnostics Details window in the Intel Advisor Survey Report.

图 3. 英特尔 Advisor 调查报告中的交互式编译器诊断详情。

即使代码可以矢量化,简单启用矢量化并不总能实现性能提升 — 即成为第 2 类和第 3 类循环。 这就是为什么必须对已经矢量化的循环进行检查,以确认其性能良好的原因。 下一节我们将简要介绍达斯伯里实验室采用英特尔Advisor 处理矢量化效率低下的循环后所实现的优化结果。

触手可及的优化:循环填充

图 4 所示为 DL MESO 配置文件中最热循环的代码。

数组 lbv 将晶格的速度保存在每个维度中,循环数变量 lbsy.nq 因此表示速度的数量。 我们案例中的模型指包含 19 个速度的三维晶格(D3Q19 模式),因此 lbsy.nq 的数值为 19。 最终形成的均衡保存在数组 feq[i] 中。

在初始分析过程中,报告称该循环是标量循环 — 即代码未经矢量化。 只需在 for 循环前面添加 #pragma omp simd,循环就实现了矢量化,且总体运行时间从 13% 降至 9%。 即使进行了这样的添加,也还有大量的优化空间。

int fGetEquilibriumF(double *feq, double *v, double rho)	
{				
  double modv = v[0]*v[0] + v[1]*v[1] + v[2]*v[2];
  double uv;				
				
  for(int i=0; i<lbsy.nq; i++)	
  {				
    uv = lbv[i*3] * v[0] 
       + lbv[i*3+1] * v[1] 
       + lbv[i*3+2] * v[2];
  
    feq[i] = rho * lbw[i] 
           * (1 + 3.0 * uv + 4.5 * uv * uv - 1.5 * modv);
   }				
  return 0;				
}				

图 4. 代码列表 — 用于计算均衡分布的循环

英特尔 Advisor 显示的最新结果表明,编译器生成了两个循环:

  • 一个矢量化循环体,矢量长度 (VL) 为 4 — 即 256 位宽 AVX 寄存器中持有 4 个双精度型
  • 一个标量余数,耗费几乎 30% 的循环时间

此类标量余数完全是不必要的开销。 这种循环余数的存在将对并行效率 — 即可以实现的最大速度提升,产生不利影响。 之所以产生如此大的剩余开销,实际上是因为循环(运行)次数不是矢量长度的整数倍。 编译器对循环进行矢量化处理时,会产生矢量化体,在我们的案例中,该矢量化体执行第 0-15 次循环迭代。 剩下的 3 次迭代 (16-18) 由标量余数代码执行。 由于总循环数非常小,因此剩下的这 3 次迭代将消耗大部分循环时间。 在实现了最佳优化的循环中,尤其是运行次数少的循环中,不应存在剩余代码。

针对这种代码我们可以使用的技巧是,增加循环迭代次数,使其成为 VL 的整数倍,即在本案例中增加为 20。 这种技巧称为 “数据填充”,也是英特尔 Advisor 在针对该循环的 Recommendations 窗口中所提出的建议(如图 5)。 为了填充数据,我们需要增加数组 feq[]、lbv[] 和 lbw[] 的大小,以便访问第 20 个(未用)位置时不会造成分段违例或类似问题。

图 11 的第 2 排举例说明了所需要进行的更改。 数值 lbsy.nqpad 是原始循环运行次数与填充值 (NQPAD_COUNT) 之和。

您还可以看到,DL_MESO 开发人员添加了 #pragma 循环次数指令。 将循环数告知编译器后,编译器将看到这个数是矢量长度的整数倍,并面向特定运行次数值优化代码生成,从而在运行时中省略标量余数调用代码。

Figure 5 The Vectorization Advisor recommendations for padding the data.

图 5. Vectorization Advisor 关于填充数据的建议。

DL_MESO 代码中许多类似的均衡分布代码结构都可通过相同方式修改。 在我们的示例中,我们修改了同一个源文件中的其他 3 个循环,每个循环都实现了 15% 的速度提升。

平衡开销和优化折衷

我们将填充技巧应用于前两个循环,但在性能和代码维护两方面付出了一定的代价。

  • 性能方面,填充可避免标量部分产生开销,但我们需要在矢量部分进行额外的计算。
  • 代码维护方面,我们需要重新安排数据结构配置,并可能需要引入依赖于工作负载的编译指示定义。

幸运的是,本案例所实现的性能收益大于性能损失,而且在代码维护方面的负担也比较轻省。

数据布局数组结构转换助力进一步提升性能

矢量化、循环填充和数据对齐技巧将 1 号热点的性能提升了 25-30%。而且,根据英特尔 Advisor4 的报告,并行矢量化效率提升了高达 56%。

由于 56% 的效率提升离理想中的 100% 还差得很远,因此达斯伯里实验室的开发人员希望进一步调查阻止循环提高效率的性能障碍。 他们重新查看了 Vector Issues/Recommendations。 这次,Vector Issues 栏突出显示了一个新的问题: Possible inefficient memory access patterns present(可能出现低效率内存访问模式)。 给出的相关建议是运行内存访问模式 (MAP) 分析。 Instruction Set Architecture/Traits 栏也提出了类似的建议(图 6)。

 Vector Issue, Trait and associated Recommendations.

图 6. “Inefficient Memory Access patterns present”: 矢量问题,特征及相关建议。

MAP 是一种深入的英特尔 Advisor 分析功能,可以识别并详细描述低效率内存访问模式的特征。 为了运行 MAP 工具,DL_MESO 优化人员采用以下基于 GUI 的模式:

  • 首先,开发人员在调查报告的第 2 栏选择相应的复选框,标记第 730 行感兴趣的循环(图 7)。
  • 之后,他们使用 Workflow 面板运行 Memory Access Pattern 集合。

Figure 7. Selecting loops for deeper MAP or Dependencies analysis.

图 7. 选择循环以深入分析 MAP 或相关性。

作为 MAP 分析结果测量的高水平 Strides Distribution 表明,单位步长和非单位固定步长访问均在该循环中进行(见图 9)。 进一步查看 MAP Problems 视图和 Source 视图,有利于识别是否出现了与借助 lbv 数组操作对应的步长-3(如果为原始标量版本)或步长-12(如果为填充后的矢量化循环)访问。

固定步长的出现意味着,从迭代到迭代,访问部分数组元素将以可预测、但非线性的方式移动。 在本案例中,步长-3 访问整数元素的 lbv 速度数组意味着在下次迭代中,访问 lbv 数组将通过 3 个整数元素移动。 3 这个值并不令人惊讶,因为相应的表达类似于 lbv[i*3+X]。

Figure 8. Inefficient memory access… Vector Issue and corresponding Recommendations.

图 8. 低效率内存访问......矢量问题和相应的建议。

非连续性固定步长对矢量化非常不利,因为它通常意味着,在矢量化代码版本中,将无法使用单个打包的内存移动指令将所有数组元素加载至最终的矢量寄存器。5 另一方面,通过采用结构数组 (AoS) - 数组结构 (SoA) 转换技巧,固定步长访问通常可以转换成单位(连续)步长访问。6 值得注意的是,运行 MAP 分析后,最初为 fGetEquilibirumF 中的循环提出的建议已自动更新为采用既定 AoS->SoA 转换的建议(图 8)。

Figure 9. Strides Distribution loop analysis and corresponding tooltip with stride taxonomy explanation.

图 9. Strides Distribution 循环分析和相应的工具提示以及步长分类法说明。

达斯伯里实验室的工程师决定就该 lbv 数组采用既定的数据布局优化方法。 它实际上是用于 fGetEquilibrium 中的循环的最新优化技巧。 为了执行这种转换,他们需要将单个 lbv 数组(包括 X、Y 和 Z 维度中的速度)替换为 3 个单独的数组:lbvx、lbvy 和 lbvz。

下图 10 和图 11 汇总了所有与填充和 AoS->SoA 相关的 DL_MESO 循环和数据结构转换,并随附了英特尔 Advisor Vectorization EfficiencyMemory Access Patterns Report 指标。

DL_MESO 工程师告诉我们,(相比于填充),尽管重构相对来说比较耗时,且无法轻易实现,但最终实现的速度提升充分说明,这种方法绝对值得一试:fGetEquilibrium 中的循环在优化型版本的基础上将速度再次提升了 2 倍。 许多其他借助 lbv 数组操作的循环也实现了类似的速度提升。

图 10. 填充和数据布局 (AoS -> SoA) 转换对 fGetEquilibriumF 中的循环产生的影响,以及英特尔Advisor Survey 分析、Trip Counts 分析和 MPA 分析的数据。


图 11. 对 fGetEquilibriumF 中的循环进行填充和数据布局 (AoS -> SoA) 转换时的相关数据分配、循环实施和英特尔 Advisor MAP 步长数据。

总结

通过使用 Vectorization Advisor 分析 DL_MESO,并为代码填充部分编译指示,Hartree Centre 成功地将前三大热点耗费的时间缩短了 10%-19%。所有优化均以 Vectorization Advisor 提出的建议为基础。 这项优化工作包括启用矢量化和采用填充优化技巧提高循环性能。 接下来,他们采用类似的技巧处理其他不太重要的热点,使应用的总体速度提升了 18%。

通过将部分变量的数据布局从结构数组转换成数组结构(同样根据 Vectorization Advisor 提出的建议),获得了进一步的性能提升。

尽管实施此次优化工作之际,Vectorization Advisor 仅适用于常规英特尔至强处理器,但如果将相同的优化方法应用于在英特尔至强融核协处理器上运行的代码,也会实现类似的加速效果 — 这显然是一个双赢的局面。

图 2 所示为常规服务器(标记为 “AVX”)和英特尔® 至强融核™ 协处理器(代号 “Knights Corner”)上某个主要关键函数所实现的加速效果。 通过这些优化方法,英特尔® 至强™ 处理器和协处理器分别实现了 2.5 倍和 4.1 倍的速度提升。

Figure 12. The impact of various optimizations (bigger is better).

图 12. 不同优化方法的影响(越大越好)。

总而言之,工程师非常高兴 Vectorization Advisor 能够帮助他们的 DL_MESO 代码实现真正的加速 — 其中一位主要开发人员表示:“这款工具令人叹服,将来在开展至强融核方面的工作时,一定会为我们提供巨大的帮助!”


1 https://software.intel.com/zh-cn/articles/intel-parallel-computing-center-at-hartree-centre-stfc
2 DL_MESO 由两个实施 Lattice Boltzmann Equation (LBE) 和 Dissipative Particle Dynamics (DPD) 方法的模拟程序包组成。 LBE 程序包支持模拟由多个液体组件、溶液和耦合热传递所组成的晶格-气体系统。
3 Vectorization Advisor 要求英特尔编译器收集一套完整的分析数据。 然而,指标的固定子集也适用于借助其他编译器构建的二进制。
4 英特尔 Advisor Vectorization Efficiency 指标目前仅适用于分析英特尔编译器 16.x (2016) 发布版所编译的代码。
5 如欲了解更多有关步长的详细信息,请观看教学视频:https://software.intel.com/zh-cn/videos/memory-access-101https://software.intel.com/zh-cn/videos/stride-and-memory-access-patterns
6 如欲了解更多有关数组结构的详细信息,请访问 https://software.intel.com/content/www/cn/zh/develop/articles/a-case-study-comparing-aos-arrays-of-structures-and-soa-structures-of-arrays-data-layouts.html

产品和性能信息

1

英特尔的编译器针对非英特尔微处理器的优化程度可能与英特尔微处理器相同(或不同)。这些优化包括 SSE2、SSE3 和 SSSE3 指令集和其他优化。对于在非英特尔制造的微处理器上进行的优化,英特尔不对相应的可用性、功能或有效性提供担保。该产品中依赖于微处理器的优化仅适用于英特尔微处理器。某些非特定于英特尔微架构的优化保留用于英特尔微处理器。关于此通知涵盖的特定指令集的更多信息,请参阅适用产品的用户指南和参考指南。

通知版本 #20110804