订阅业界RSS CSDN首页> 业界

英特尔® 软件工具的浮点再现性

发表于2019-03-19 10:53| 来源intel| 作者intel

摘要:克服不确定性 大多数实数的二进制浮点 (FP) 表示都不准确,涉及浮点数的大多数计算结果都具有先天的不确定性。因此,在不同情况下重复计算可能会生成不同的结果,尽管结果一直在预期的不确定性范围内。这通常不是大问题,但是某些上下文需要再现性克服这种不确定性的(如质量保证、法律问题或...

克服不确定性

大多数实数的二进制浮点(FP) 表示都不准确,涉及浮点数的大多数计算结果都具有先天的不确定性。因此,在不同情况下重复计算可能会生成不同的结果,尽管结果一直在预期的不确定性范围内。这通常不是大问题,但是某些上下文需要再现性克服这种不确定性的(如质量保证、法律问题或功能安全要求)。但是,提高或精确的再现性通常以牺牲性能为代价。

什么是再现性? 

再现性对于不同人而言有着不同的意义。最基本的含义是,使用相同的处理器对相同的数据重复运行相同的可执行文件时,应该总是生成完全相同的结果。它有时被称作可重复性或连续运行再现性。当用户了解到这并不是自动的,结果也不一定确定时,有时会感到惊讶,甚至震惊。

再现性还有另一个含义,即针对和/或在不同类型的处理器上运行,以不同的优化水平构建或运行不同类型或程度的并行化时,得到相同的结果。

这有时被称作条件数值再现性(conditional numerical reproducibility)。完全可再现结果所需的条件取决于上下文,可能导致性能下降。

在默认情况下,许多软件工具不提供完全可再现结果。

  变化的来源

 FP 结果变化的主要来源为优化。优化包括:

 

    在构建或运行时以特定处理器和指令集为目标

   各种并行化形式

 

在现代处理器上,巨大的性能优势使用户难以对大型应用优化无动于衷。准确性差异的原因包括:

 

    数学函数或运算的不同近似值,如除法

    计算与存储中间结果的准确性

    被视为零的反规范化(非常小的)结果

    特殊指令的使用,如融合乘加(FMA) 指令

 

特殊指令通常比它们替代的单独的乘加指令更准确,但是后果是最终结果可能发生改变。

对于英特尔® 高级向量扩展指令集2(英特尔® AVX2)及更高版本,FMA 生成是在O1 及以上级别运行的优化。它不在语言标准范畴内,因此在不同的上下文中,编译器的优化方式可能有所不同(如不同的处理器目标,即使两个目标均支持FMA 指令)。

最重要的变化来源可能是运算顺序的变化,对于并行应用尤为如此。尽管不同的顺序可能在数学上是相等的,但是在有限精度算法中,舍入误差以不同的方式变化和累加。尽管用户有时认为未经优化的结果是正确的,但是不同的结果并不一定意味着不准确。

1所示的变换为例,编译器可能进行变换以提升性能。

 

示例编译器变换

到目前为止,我们考虑的优化对顺序和并行应用的影响相似。可通过编译器选项控制或禁止编译代码。

归约

归约展示了FP 运算的顺序如何决定结果,这是一个特别重要的示例。我们以求和为例,但是我们讨论的内容也适用于其他归约,如乘积、最大值和最小值。求和的并行实施将它们分解为部分和,每线程(例如,对于OpenMP*)、每进程(例如,对于MPI)或每SIMD 通道(对于向量化)一个部分和。然后,所有部分和都可以安全地并行增加。2显示了一个示例。

使用归约的并行求和

请注意,的元素相加的顺序以及中间结果对机器精度的舍入在两个示例中有很大的不同。如果正项和负项之间有较大的抵消,对最终结果的影响将非常大。用户通常认为第一个串行版本是“正确”结果,但是带多个部分和的并行版本减少了舍入误差的累加,并给出了更接近无限精度的结果,对于大量元素尤其如此。并行版本运行速度更快。

 归约可再现吗?

如果想要归约可再现,部分和的组成不可改变。对于向量化,这意味着向量长度不可改变。对于OpenMP,这意味着线程数量必须为常量。对于英特尔®MPI ,这意味着队列数量不可改变。此外,部分和必须以相同、固定的顺序相加。对于向量化,这些操作将自动进行。

对于OpenMP 线程化,标准支持部分和以任何顺序组合。在英特尔实施中,低线程数量(在英特尔®至强处理器上小于个,在英特尔®至强融核™处理器上小于个)默认先到先得。为了确保以固定顺序将部分和相加,您应该设置环境变量KMP_DETERMINISTIC_ REDUCTION=true 并使用静态调度(默认的调度协议)。

英特尔®线程构建模块(TBB)使用动态调度,因此,parallel_reduce()方法不生成连续运行可再现结果。但是,另一种方法parallel_deterministic_reduce() 可以支持。这创建了用于计算部分和的固定任务,然后创建固定的有序树来组合它们。动态调度程序可以视情况调度任务,前提是该程序遵守它们之间的依赖性。这样不仅在相同的环境中每次运行时可以生成可再现结果,而且即使在工作线程数量变化的情况下,还确保结果保持可再现性。(OpenMP 标准不适用于基于固定树的类似归约运算,但是可以使用OpenMP 任务和依赖性编写该运算。)

对于英特尔MPI 库,我们可以根据MPI 队列在处理器节点中的分布,优化部分结果的组合顺序。获得可再现结果的唯一其他方法是从一组有限的非拓扑感知型归约算法中选择(见下)。

在我们介绍的示例中,并行或向量化归约的结果通常不同于顺序结果。如果不能接受的话,必须使用单个线程、进程或SIMD 通道执行归约。对于后者,这意味着使用/fp:precise (Windows*) -fp-model preciseLinux* macOS*)编译,以确保归约循环不会自动向量化。

英特尔®编译器

推荐高级选项/fp:consistent (Windows) fp-model-consistentLinux macOS),以便在运行之间、不同的优化等级之间和相同架构的不同处理器类型之间实现最佳再现性。它等同于设置选项/Qfma-(-no-fma)(以禁用FMA 生成)、/Qimf-arch-consistency:true(-fimf-arch-consistency=true)(将数学函数限制为在所有处理器类型中提供相同结果的实施)以及/fp:precise(-fp-model-precise)(以禁用可能导致结果变化的其他编译器优化)。

再现性会影响性能。影响程度与应用有关,但是性能下降10% 比较常见。通常对拥有大量可向量化循环的计算密集型应用影响最大,这些应用包含浮点归约或超越数学函数调用。有时为了降低影响,可以添加选项/Qimf-use-svml(-fi-use-svml) ,这样可将短向量数学库用于数学函数的标量调用和向量调用,从而确保一致性以及重新支持包含数学函数的循环的自动向量化。

默认选项/fp:fast(-fp-model fast) 支持编译器进行优化,不用考虑再现性。如果唯一的要求是可重复性即在相同的处理器上使用相同的数据重复运行相同的可执行文件将生成相同的结果,那么使用

/Qopt-dynamic-align-(-qno-opt-dynamic-align) 进行重新编译可能就足够了。这仅禁用在运行时测试数据对齐的剥离循环的生成。相比上述/fp(-fp-model) 选项,它对性能的影响要小很多。 

不同编译器和操作系统之间的再现性

由于大多数数学函数的结果缺少公认的要求,编译器和操作系统之间的再现性受限。遵守数学函数的最终标准(如需要准确舍入的函数)将提高一致性,但是会大幅降低性能。

目前,对于针对不同操作系统(如Windows Linux)的代码,无法系统地测试结果再现性。选项/Qimf-use-svml fimf-use-svml找到了特定的已知差异来源,这些差异与包含数学函数的循环向量化相关。建议使用这些选项提高Windows Linux 上浮点结果之间的一致性。

无法确保使用不同英特尔® 编译器主要版本的应用构建之间的一致性。改进数学库函数的实施可能使结果更准确,但却不同于之前的实施。/Qimf-precision:high(-fimf-precision=high) 选项可能减少此类差异。同样,无法确保使用英特尔® 编译器的构建和使用其他厂商编译器构建之间的再现性。使用/fp:consistent(-fp-model consistent) 等选项和其他编译器的同等选项可帮助缩小编译代码造成的差异。在可能的情况下,两个编译器使用相同的数学运行时库也会缩小差异。

英特尔® 数学核心函数库

英特尔®数学核心函数库(英特尔®MKL包含高度优化的函数,用于线性代数、快速傅立叶变换、稀疏矩阵解算器、统计分析以及使用OpenMP TBB 在内部进行向量化或线程化的其他领域。在默认情况下,由于优化函数内运算顺序的变化,相同处理器上的重复运行不会得出相同的结果。英特尔® MKL 函数检测它们运行的处理器并执行针对处理器优化的代码,因此,不同处理器上的重复运行会得出不同的结果。为了解决这个问题,英特尔® MKL 实施了条件数值再现性。条件为:

    使用OpenMP(而非TBB)上的英特尔® MKL 版本

    保持线程数量不变

    使用静态调度(OMP_SCHEDULE=static,默认值)

    禁用活跃线程数量的动态调整(OMP_DYNAMIC=falseMKL_DYNAMIC=false,默认值)

    使用相同的操作系统和架构(如英特尔64 Linux

    使用相同的微架构或指定最小的微架构

最小微架构可通过函数或子例程调用(如mkl_cbwr_set (MKL_CBWR_AVX)或通过设置运行时环境变量(MKL_CBWR_BRANCH=MKL_CBWR_AVX)指定。因此,能够在支持英特尔® AVX 或后续指令集(如英特尔® AVX2 或英特尔® AVX-512)的任何英特尔® 处理器上得到一致的结果,但是在支持更高级指令集的处理器上有可能降低性能。参数MKL_CBWR_COMPATIBLE 可在任何英特尔处理器或相同架构上兼容的非英特尔处理器上得到一致的结果。参数MKL_CBWR_AUTO 支持使用与运行时检测到的处理器对应的代码路径。它确保该处理器上的重复运行生成相同的结果,但是在其他处理器类型上生成的结果可能会不同。如果运行时处理器不支持特定的最小微架构,可执行文件仍运行,但是采用与实际运行时微架构对应的代码路径,就像指定了MKL_CBWR_AUTO。结果可能不同于在其他处理器上得到的结果,不会发出警告。

对于计算密集型英特尔® MKL 函数,限制指令集有时会对性能造成巨大的影响。显示对于不同的最小微架构选择,在英特尔®至强®可扩展处理器上执行DGEMM 矩阵乘法的相对性能不断下降。 

1.指令集架构(ISA) 对在英特尔® 至强® 可扩展处理器上运行的DGEMM 的影响

目标 ISA

相对性能估算值

MKL_CBWR_AUTO

1.0

MKL_CBWR_AVX512

1.0

MKL_CBWR_AVX2

0.50

MKL_CBWR_AVX

0.27

MKL_CBWR_COMPATIBLE

0.12

 英特尔®MPI 

使用英特尔® MPI 库得到的结果是可再现的,前提是:

    编译代码和库调用遵守编译器和库的可再现条件

    MPI 和集群环境中的任何方面都没有变化,包括队列数量和处理器类型

 

像往常一样,集合运算(如求和)和其他归约对环境中的细微调整最敏感。许多集合运算的实施根据MPI 队列在集群节点上的分布而优化,这将改变运算顺序,导致结果的变化。英特尔® MPI 库支持条件数值再现性,从这个角度来讲,应用将从相同的二进制代码中得到可再现结果,即使在节点队列分布不同的情况下。这需要选择非拓扑感知型算法,即不根据节点上队列分布进行优化的算法,使用的环境变量为I_MPI_ADJUST_ 系列:

 

    I_MPI_ADJUST_ALLREDUCE

    I_MPI_ADJUST_REDUCE,

    I_MPI_ADJUST_REDUCE_SCATTER

    I_MPI_ADJUST_SCAN

    I_MPI_ADJUST_EXSCAN

    等等

 

例如,英特尔® MPI 库开发人员参考记录了11 种不同的MPI_REDUCE() 实施,

2列出了前七种实施。

 2.不同队列分布的MPI_REDUCE() 结果对比

 

Rank Distribution Algorithm

队列分布算法

0246 node1

1357 node2

0246 node1

1357 node2

All node1

All node1

0145 node1

2367 node2

0145 node1

2367 node2

0123 node1

4567 node2

0123 node1

4567 node2

Shumilin’s

Shumilin 算法

Binomial

二项式

Topology-aware Shumilin’s

拓扑感知型Shumilin 算法

Topology-aware binomial

拓扑感知型二项式

Rabenseifner’s

Rabenseifner 算法

Topology-aware Rabenseifner’s

拓扑感知型Rabenseifner 算法

Knomial

Knomial

英特尔®酷睿™i5-4670T 处理器两个节点(每个节点2.30 GHz核、8 GB 内存),一个运行Red Hat* EL 6.5,另一个运行Ubuntu* 16.04。所用的示例代码节选自The Parallel Universe21 期的《英特尔®MPI 库条件再现性》(参见下文参考文献)。

比较了示例程序中一系列MPI_REDUCE() 实施和MPI 队列在两个集群节点上种不同分布的结果。种颜色对应观察到的个不同结果。结果的差异非常小,接近精度的极限,但是更大计算中的取消有时会放大细微的差异。

无论队列在节点上的分布如何,独立于拓扑的实施都会给出相同的结果,而拓扑感知型实施则不会。MPI_REDUCE(未显示)的默认实施是基于工作负载和拓扑的混合算法。它还提供了随节点上队列分布而变化的结果。

优势

英特尔® 软件开发工具提供了在明确定义的条件下获得可再现FP 结果的方法。

参考资料

1.《使用英特尔®编译器实现浮点结果的一致性》

2.《面向Linux* 的英特尔®核心函数库2019的“获得数值可再现结果”部分

3.英特尔®MPI 库条件再现性》,The Parallel Universe21 

4.《调优英特尔MPI 库:基本技术》,的“调优数值稳定性”部分