我可以保证 C++编译器不会重新排序我的计算吗?
我目前正在阅读优秀的 双双和四双算术库论文,在前几行中,我注意到它们按以下方式执行求和:
std::pair<double, double> TwoSum(double a, double b)
{
double s = a + b;
double v = s - a;
double e = (a - (s - v)) + (b - v);
return std::make_pair(s, e);
}
误差的计算,e
,依赖于事实上,由于 IEEE-754 浮点数学的非关联属性,计算完全遵循该操作顺序。
如果我在现代优化 C++ 编译器(例如 MSVC 或 gcc)中编译它,我可以确保编译器不会优化此计算的完成方式吗?
其次,这是吗?在 C++ 标准内的任何地方都得到保证?
I'm currently reading through the excellent Library for Double-Double and Quad-Double Arithmetic paper, and in the first few lines I notice they perform a sum in the following way:
std::pair<double, double> TwoSum(double a, double b)
{
double s = a + b;
double v = s - a;
double e = (a - (s - v)) + (b - v);
return std::make_pair(s, e);
}
The calculation of the error, e
, relies on the fact that the calculation follows that order of operations exactly because of the non-associative properties of IEEE-754 floating point math.
If I compile this within a modern optimizing C++ compiler (e.g. MSVC or gcc), can I be ensured that the compiler won't optimize out the way this calculation is done?
Secondly, is this guaranteed anywhere within the C++ standard?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(10)
您可能想查看 g++ 手册页: http://gcc.gnu.org/onlinedocs/gcc-4.6.1/gcc/Optimize-Options.html#Optimize-Options
特别是
-fassociative-math
、-ffast-math
和-ffloat-store
根据 g++ 手册,除非您特别要求,否则它不会重新排序您的表达式它。
You might like to look at the g++ manual page: http://gcc.gnu.org/onlinedocs/gcc-4.6.1/gcc/Optimize-Options.html#Optimize-Options
Particularly
-fassociative-math
,-ffast-math
and-ffloat-store
According to the g++ manual it will not reorder your expression unless you specifically request it.
是的,这是安全的(至少在这种情况下)。您只在那里使用两个“运算符”,主要表达式
(something)
和二进制something +/- Something
(附加)。1.9 程序执行
(C++0x N3092)节指出:在分组方面,
5.1 主要表达式
指出:我相信在引用中使用“相同”一词需要一个一致的实现来保证它将按指定的顺序执行,除非另一个订单可以给出完全相同相同的结果结果。
对于加法和减法,
5.7 加法运算符
部分有:因此,标准规定了结果。如果编译器可以确定使用不同的操作顺序可以获得相同的结果,那么它可能会重新排列它们。但无论这种情况发生与否,你都无法辨别出差异。
Yes, that is safe (at least in this case). You only use two "operators" there, the primary expression
(something)
and the binarysomething +/- something
(additive).Section
1.9 Program execution
(of C++0x N3092) states:In terms of the grouping,
5.1 Primary expressions
states:I believe the use of the word "identical" in that quote requires a conforming implementation to guarantee that it will be executed in the specified order unless another order can give the exact same results.
And for adding and subtracting, section
5.7 Additive operators
has:So the standard dictates the results. If the compiler can ascertain that the same results can be obtained with different ordering of the operations then it may re-arrange them. But whether this happens or not, you will not be able to discern a difference.
这是一个非常合理的担忧,因为广泛使用的英特尔 C++ 编译器默认执行可能会改变结果的优化。
参见http://software.intel.com/sites/products/documentation/hpc/compilerpro/en-us/cpp/lin/compiler_c/copts/common_options/option_fp_model.htm#option_fp_model
This is a very valid concern, because Intel's C++ compiler, which is very widely used, defaults to performing optimizations that can change the result.
See http://software.intel.com/sites/products/documentation/hpc/compilerpro/en-us/cpp/lin/compiler_c/copts/common_options/option_fp_model.htm#option_fp_model
如果任何编译器错误地假定算术运算符与默认优化选项的关联性,我会感到非常惊讶。
但要警惕 FP 寄存器的扩展精度。
有关如何确保 FP 值不具有扩展精度的信息,请参阅编译器文档。
I would be quite surprised if any compiler wrongly assumed associativity of arithmetic operators with default optimising options.
But be wary of extended precision of FP registers.
Consult compiler documentation on how to ensure that FP values do not have extended precision.
如果你真的需要,我认为你可以制作一个 noinline 函数 no_reorder(float x) { return x; },然后用它代替括号。显然,这并不是一个特别有效的解决方案。
If you really need to, I think you can make a noinline function no_reorder(float x) { return x; }, and then use it instead of parenthesis. Obviously, it's not a particularly efficient solution though.
一般来说,您应该能够——优化器应该了解实际操作的属性。
也就是说,我会测试我正在使用的编译器。
In general, you should be able to -- the optimizer should be aware of the properties of the real operations.
That said, I'd test the hell out of the compiler I was using.
是的。编译器不会改变这样的块中的计算顺序。
Yes. The compiler will not change the order of your calculations within a block like that.
在编译器优化和处理器上的无序执行之间,几乎可以保证事情不会完全按照您的命令发生。
然而,也保证这永远不会改变结果。 C++ 遵循标准操作顺序,并且所有优化都保留此行为。
底线:不用担心。编写数学上正确的 C++ 代码并信任编译器。如果出现任何问题,几乎可以肯定问题不在于编译器。
Between compiler optimizations and out-of-order execution on the processor, it is almost a guarantee that things will not happen exactly as you ordered them.
HOWEVER, it is also guaranteed that this will NEVER change the result. C++ follows standard order of operations and all optimizations preserve this behavior.
Bottom line: Don't worry about it. Write your C++ code to be mathematically correct and trust the compiler. If anything goes wrong, the problem was almost certainly not the compiler.
根据其他答案,您应该能够依赖编译器做正确的事情 - 大多数编译器允许您编译和检查汇编器(对 gcc 使用 -S ) - 您可能希望这样做以确保您得到您期望的操作顺序。
不同的优化级别(在 gcc、-O _O2 等中)允许重新排列代码(但是像这样的顺序代码不太可能受到影响)——但我建议您应该将代码的特定部分隔离到单独的文件中,这样您就可以控制计算的优化级别。
As per the other answers you should be able to rely on the compiler doing the right thing -- most compilers allow you to compile and inspect the assembler (use -S for gcc) -- you may want to do that to make sure you get the order of operation you expect.
Different optimization levels (in gcc, -O _O2 etc) allows code to be re-arranged (however sequential code like this is unlikely to be affected) -- but I would suggest you should then isolate that particular part of code into a separate file, so that you can control the optimization level for just the calculation.
简短的答案是:编译器可能会改变计算的顺序,但它永远不会改变程序的行为(除非您的代码使用具有未定义行为的表达式:http://blog.regehr.org/archives/213)
但是,您仍然可以通过停用所有编译器优化(选项“-O0”)来影响此行为和海湾合作委员会)。如果您仍然需要编译器来优化其余代码,您可以将此函数放在单独的“.c”中,您可以使用“-O0”对其进行编译。
此外,您还可以使用一些技巧。例如,如果您将代码与 extern 函数调用交错,编译器可能会认为重新排序代码是不安全的,因为该函数可能具有未知的副作用。调用“printf”来打印中间结果的值将导致类似的行为。
无论如何,除非您有任何很好的理由(例如调试),否则您通常不想关心这一点,并且您应该信任编译器。
The short answer is: the compiler will probably change the order of your calculations, but it will never change the behavior of your program (unless your code makes use of expression with undefined behavior: http://blog.regehr.org/archives/213)
However, you can still influence this behavior by deactivating all compiler optimizations (option "-O0" with gcc). If you still needs the compiler to optimize the rest of your code, you may put this function in a separate ".c" which you can compile with "-O0".
Additionally, you can use some hacks. For instance, if you interleaves your code with extern function calls the compiler may consider that it is unsafe to re-order your code as the function may have unknown side-effect. Calling "printf" to print the value of your intermediate results will conduct to similar behavior.
Anyway, unless you have any very good reason (e.g. debugging) you typically don't want to care about that, and you should trust the compiler.