使用Intel编译器获得一致的浮点数值计算结果
使用Intel编译器获得一致的浮点数值计算结果
大多数十进制的浮点数, 用二进制表示时不是完全一致的; 与此同时, 大多数与浮点数值相关的计算结果, 存在着固有的不确定性.
通常, 编写浮点计算应用软件希望达到如下的目标:
- 准确性:
意味着该产品产生的计算结果,应当”接近”于实际计算的结果; 评判的标准是误差值, 有时候也采用最后几位(“units in the last place”, ulp)
- 可复制性:
意味着该产品始终产生一致的结果, 无论运行的先后, 采用不同的编译选项, 使用不同的编译器, 还是在不同的处理器或者操作系统平台
- 性能:
计算应用当然运行得越快越好
但是, 这些目标往往是互相矛盾的. 而良好的编程习惯和合理地使用编译器选项, 能够帮助你很好地取得平衡. 正确的编译器选项, 可以得到一致的, 近乎可重现的结果, 也能保证足够快的性能(当然不是最佳的).
浮点运算规则
Intel编译器实现了一整套Microsoft引入的浮点运算规则模型, 具体内容可参考 Microsoft* Visual C++* Floating-Point Optimization: http://msdn2.microsoft.com/en-us/library/aa289157(vs.71).aspx
这个编译器开关选项 (Windows中使用/fp:, Linux 或 Mac OS X 使用-fp-model, 下同) 可以让你自由地选择最合适的编译器规则, 基于如下的条件:
- 数值安全 (value safety)
- 浮点表达式赋值 (FP expression evaluation)
- 浮点精度溢出 (precise FP exceptions)
- 浮点计算合并(FP contractions)
- 访问浮点运算单元(FPU)
如下列出了使用/fp: (-fp-model) 时的全部可选参数:
precise 只允许数值安全的优化
source | double | extended 指定浮点表达式赋值时使用的精度
except 激活严格的浮点溢出模式
strict 激活访问FPU; 不允许浮点计算合并, 比如乘加(fused multiply-add, fma); 默认使用 ‘prcise’ 和 ‘except’
fast[=1] (缺省选项) 允许数值不安全的优化, 编译器自行决定表达式赋值的精度; 不允许访问FPU; 允许浮点计算合并
fast[=2] 允许一些额外的近似值计算
这个编译器开关取代了一系列在旧版本的Intel编译器中使用的选项, 例如/Op, /QIPF-fltacc, /flt-consistency (-mp, -IPF-fltacc, -flt-consistency).
其中, 当浮点运算结果需要满足符合ANSI / IEEE C++ 和 Fortran 语言标准时, 推荐使用如下的编译器选项组合:
/fp:precise /fp:source (Windows)
-fp-model precise -fp-model source (Linux, Mac OS X)
一个Fortran应用的例子
与不使用优化编译选项相比, 该应用程序优化编译后产生了不同的结果, 并且这种差异以一种特定的形式不断扩大. 产生问题的根源被定位在代码中如下形式的表达式:
A(I) + B + TOL
相对于TOL是一个非常小的正数, A(I) 和 B 的值比较大. 当编译器生成优化代码时, 愿意采用如下的赋值形式:
A(I) + (B + TOL)
这样的话, 常数表达式 (B+TOL) 进入循环 I 之前就可以只需要赋值一次. 可是原来代码的目的是为了保证表达式的结果为正, 当 A(I) 约等于 –B. 一旦 TOL 直接与 B 相加, 由于浮点精度限制, 它往往会被就会直接被取整, 因此也就无法发挥它使得表达式的结果保持正数的作用.
最简单的解决办法就是, 使用编译器开关 –fp-model precise 重新编译受影响的代码文件. 这样就能防止表达式重新组合, 而是按照代码书写时的次序进行赋值.
当然, 更直接的解决方案, 不会带来过多的性能下降, 就是改写原始的代码为:
(A(I) + B) + TOL
这样就可以清楚地表达出编程者的意图, 同时别忘了编译时使用选项 –assume protect_parents
另一个WRF的例子 (Weather Research and Forecasting model)
我们观察到同一个应用运行在不同数量的处理器下的MPI(Messenge Passing Interface)环境中, 结果会出现细微的不同. 这是因为随着不同数量的MPI进程, 原始问题的分解方式会发生变化, 进而导致循环边界和数据对齐也相应变化. 结果导致循环向量化代码中的循环次数, 以及前后的处理代码都发生了变化, 最终导致同样的数据, 却产生了略微不同的计算结果.
解决的方法就是编译时使用 –fp-model precise. 这样会让编译器生成前后一致的代码和数学库调用, 无论在循环体中和循环前后.
有时候(并不是一定), 这样做会阻止循环被向量优化, 特别是由于可能会改变计算的顺序导致结果发生结果不同, /fp:precise (-fp-model precise) 选项禁止归约(reduction)计算的向量化或者自动并行化. 注意, OpenMP中的并行归约操作无法因此被自动禁止, 程序员应当知道它们是数值不安全的操作, 必须自行处理.
可能的性能影响
/fp:precise /fp:source (-fp-model precise –fp-model source) 禁止使用某些特定的优化, 因此会在一定程度上降低应用程序的执行速度. 不同类型的应用受到的影响可能会非常不同.
从SPECCPU2000fp 测试集的试验数据来看, 对原有 -O3 选项的性能影响比较大, 大约为15%-20%, 主要是其中许多高级别的循环优化往往需要重新排列操作顺序. 而对 –O2 的影响很小, 基本在 1% 左右.
额外注意事项
编译选项 /fp:precise /fp:source (-fp-model precise –fp-model source) 也应当在调试模式 /Od (-O0) 时使用. Intel 编译器11.1以前的版本中, /Od (-O0) 会默认使用 /Op (-mp), 因此即使在 Intel 64 架构中也同样会生成 x87 指令, 除非使用 /fp:source (-fp-model source).
从11.0编译器版本开始, 使用/fp:precise (-fp-model precise)后, 循环中包含数学函数例如: log(), sin(), 也不会自动被向量化. 因为它可能会导致一个函数调用不同的数学函数库, 从而返回与标准数学函数库不同的, 略有差别的结果. 编译器选项开关 /Qfast-transcendentals (-fast-transcendentals) 能够恢复原来 10.1 版本中的缺省行为, 重新激活向量化优化.
结束语
Intel编译器选项能够让你控制并在浮点数值计算的准确性, 可复制性和性能目标中自行取舍.
使用 /fp:precise /fp:source (Windows) 或者 –fp-model precise –fp-model source (Linux, Mac OS X) 能够显著提高浮点运算结果的准确性和可复制性, 同时降低对运算性能的影响.
进一步阅读信息
1) Dr. Martyn J. Corden, David Kreitzer: “Consistency of Floating-Point Results using the Intel? Compiler or Why doesn’t my application always give the same answer?” http://software.intel.com/en-us/articles/consistency-of-floating-point-results-using-the-intel-compiler/
2) Microsoft Visual C++* Floating-Point Optimization: http://msdn2.microsoft.com/en-us/library/aa289157(vs.71).aspx
3) The Intel? C++ and Fortran Compiler User and Reference Guides, “Floating Point Operations” section.
4) “Floating Point Calculations and the ANSI C, C++ and Fortran Standard” http://www.intel.com/cd/software/products/asmo-na/eng/330130.htm
or see the list at http://www.intel.com/cd/software/products/asmo-na/eng/330130.htm
使用Intel编译器获得一致的浮点数值计算结果的更多相关文章
- Linux下用Intel编译器编译安装NetCDF-Fortan库(4.2以后版本)
本来这个问题真的没必要写的,可是真的困扰我太久%>_<%,决定还是记录一下. 首先,最权威清晰的安装文档还是官方的: Building the NetCDF-4.2 and later F ...
- x64内联汇编调用API(需intel编译器,vc不支持x64内联汇编)
#include "stdafx.h" #include <windows.h> STARTUPINFOW StartInfo = {0}; PROCESS_INFO ...
- kernel 对浮点的支持
http://blog.chinaunix.net/uid-22545494-id-316735.html 作者: Sam(甄峰) sam_code@hotmail.com 一:早期ARM上的浮点模 ...
- 【转】浮点格式IEEE754详解
原文网址:http://www.cnblogs.com/zjujunge/archive/2012/09/13/2682613.html Intel聘请了最好的数值分析家来为8087FPU设计浮点数格 ...
- C++ ABI之名字改变,编译器生成符号研究(以Qt为例)
在C++中,由于重载等技术的存在,编译器要将函数.结构体.类等等的信息传递给链接器,就不能像C语言那样简单地通过函数名来完成,它需要提供额外的参数信息,而还要和C语言共用链接器,这就需要用到名字改编( ...
- Intel MPI 配置与基本使用
安装 Document 系统配置/含NFS 编译环境 设置 加载 mpivars.[c]sh 脚本. 创建文本文件 mpd.hosts ,其中保存有集群的节点列表,每行一个名字 (只针对开发者) 确保 ...
- 为编译器的实现者提供一个精确的定义:ANSI C
编译器的实现 常用C++编译器推荐_w3cschool https://www.w3cschool.cn/cpp/cpp-zxm72ps8.html 常用C++编译器推荐 由 Alma 创建, 最后一 ...
- 【转载】HPL与HPCG测试(一)
来源:HPL与HPCG测试 (一) 一.HPL与HPCG 简介 1.HPL HPL 即 High Performance Linpack,它是针对现代并行计算集群的测试工具.用户不修改测试程序,通过调 ...
- Java核心技术卷阅读随笔--第3章【Java 的基本程序设计结构】
Java 的基本程序设计结构 现在, 假定已经成功地安装了 JDK,并且能够运行第 2 章中给出的示例程序.我们从现在开始将介绍 Java 应用程序设计.本章主要介绍程序设计的基本概念(如数据类型.分 ...
随机推荐
- SqlHelp
using System.Configuration;using System.Data; public class SqlHelp { private static string connectio ...
- 高效操作DOM
DOM树与Render树 这个应该都是知道的.就是用户请求HTML下来后,浏览器渲染引擎的基本工作中两个概念. copy一张图,流程大概就是:解析html构建DOM树,渲染树构建,渲染树布局,绘制渲染 ...
- [转] iOS多线程编程之Grand Central Dispatch(GCD)介绍和使用
介绍: Grand Central Dispatch 简称(GCD)是苹果公司开发的技术,以优化的应用程序支持多核心处理器和其他的对称多处理系统的系统.这建立在任务并行执行的线程池模式的基础上的.它首 ...
- VC++ win32 多线程 一边画圆一边画矩形
// WinThreadTest.cpp : Defines the entry point for the application. // #include "stdafx.h" ...
- docker4dotnet
docker4dotnet #1 – 前世今生 & 世界你好 作为一名.NET Developer,这几年看着docker的流行实在是有些眼馋.可惜的是,Docker是基于Linux环境的 ...
- Protel99se教程九:protel99se中PCB设计的高级应用
在上一节我们PCB资源网的protel99se教程当中,我们给大家讲解了在protel99se进行原理图设计中的一些高级应用技巧,在这一节protel99se教程当中,我们将给大家讲解的是,在prot ...
- FreeBSD 10安装KDE桌面环境简介(亲测bsdconfig命令有效)
FreeBSD 10出来一段时间了,自己摸索装上KDE环境,网上介绍的都是10以前版本的,要么对现在的不合适,走了一大圈弯路还是装不好:要么太繁琐且装了一堆无用的软件.本着让更多人能快速方面的入门Fr ...
- DLL与EXE之间的通讯调用 以及 回调函数的线程执行空间
dll 与 exe 之间的通讯方式有很多种, 本文采用回调函数的方法实现, 本文也将研究多线程,多模块的情况下,回调函数所在的线程, 啥也不说了,先附上代码: 下面的是dll模块的的, dll的工程文 ...
- poj 1204 Word Puzzles(字典树)
题目链接:http://poj.org/problem?id=1204 思路分析:由于题目数据较弱,使用暴力搜索:对于所有查找的单词建立一棵字典树,在图中的每个坐标,往8个方向搜索查找即可: 需要注意 ...
- GDAL1.9.1 IN VS2008 C#中的编译及使用
下载gdal1.9.1到官网:http://www.gdal.org/ GDAL库的简洁.高效深受开发人员的喜爱,很多开源的GIS软件甚至是商业GIS软件都使用了这个库.GDAL使用C++,在Visu ...