我能保证c++编译器不会重新排序我的计算吗?

时间:2022-11-27 17:10:01

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.

误差e的计算依赖于这样一个事实:由于IEEE-754浮点数的非结合性,计算完全遵循运算的顺序。

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?

如果我在一个现代的优化c++编译器(例如MSVC或gcc)中编译它,我能保证编译器不会优化计算的方式吗?

Secondly, is this guaranteed anywhere within the C++ standard?

第二,这在c++标准中有保证吗?

10 个解决方案

#1


4  

Yes, that is safe (at least in this case). You only use two "operators" there, the primary expression (something) and the binary something +/- something (additive).

是的,那是安全的(至少在这种情况下)。这里只使用两个“运算符”,即主表达式(某)和二进制的+/-某(添加剂)。

Section 1.9 Program execution (of C++0x N3092) states:

第1.9节程序执行(C+ 0x N3092)

Operators can be regrouped according to the usual mathematical rules only where the operators really are associative or commutative.

只有当运算符是结合的或可交换的时,运算符才能根据通常的数学规则重新分组。

In terms of the grouping, 5.1 Primary expressions states:

在分组方面,5.1主要表述如下:

A parenthesized expression is a primary expression whose type and value are identical to those of the enclosed expression. ... The parenthesized expression can be used in exactly the same contexts as those where the enclosed expression can be used, and with the same meaning, except as otherwise indicated.

圆括号表达式是一种主要表达式,其类型和值与所包含表达式的值相同。括号内的表达式可以在与所包含的表达式相同的上下文中使用,并且具有相同的含义,除非另有说明。

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:

对于加减法,第5.7节加算符有:

The additive operators + and - group left-to-right.

加性运算符+和-组从左到右。

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.

标准决定结果。如果编译器可以确定在不同的操作顺序下可以得到相同的结果,那么它可以重新排列它们。但无论这种情况是否发生,你都无法分辨出其中的差别。

#2


6  

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

您可能想看一下g++手册页:http://gcc.gnu.org/onlinedocs/gcc-4.6.1/gcc/Optimize-Options.html#优化选项。

Particularly -fassociative-math, -ffast-math and -ffloat-store

特别是-fassociative math, - ffastt -math和-ffloat-store

According to the g++ manual it will not reorder your expression unless you specifically request it.

根据g++手册,除非你特别要求,否则它不会重新排列你的表达式。

#3


5  

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.

这是一个非常合理的问题,因为英特尔的c++编译器(它被广泛使用)默认执行可以改变结果的优化。

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

看到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

#4


2  

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.

但是要注意FP寄存器的扩展精度。

Consult compiler documentation on how to ensure that FP values do not have extended precision.

请参阅编译器文档,了解如何确保FP值不具有扩展精度。

#5


0  

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.

也就是说,我要测试我使用的编译器。

#6


0  

Yes. The compiler will not change the order of your calculations within a block like that.

是的。编译器不会在这样的块中改变计算顺序。

#7


0  

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.

然而,它也保证这永远不会改变结果。c++遵循标准的操作顺序,所有的优化都保留这种行为。

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.

底线:别担心。编写c++代码,使其在数学上正确,并信任编译器。如果出了什么问题,问题几乎肯定不是编译器的问题。

#8


0  

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.

如果您确实需要,我认为您可以创建noinline函数no_reorder(float x) {return x;},然后用它代替括号。显然,这不是一个特别有效的解决方案。

#9


-1  

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.

按照其他答案你应该能够依靠编译器做正确的事情——大多数编译器允许您编译和检查汇编程序(使用- s gcc)——你可能想这样做,以确保你得到你期望的顺序操作。

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.

不同的优化级别(gcc - o _O2等)允许将代码重新(然而顺序代码这样不太可能受到影响),但我建议你应该那么隔离特定部分的代码到一个单独的文件,这样你可以控制优化级别的计算。

#10


-1  

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)

简单的回答是:编译器可能会改变计算的顺序,但它永远不会改变程序的行为(除非您的代码使用了未定义的行为的表达式: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.

但是,您仍然可以通过禁用所有编译器优化(选项“-O0”和gcc)来影响这种行为。如果您仍然需要编译器来优化其余的代码,您可以将这个函数放在单独的“。c,你可以用-O0编译。此外,您还可以使用一些技巧。例如,如果您在代码中插入了extern函数调用,编译器可能会认为重新排序代码是不安全的,因为该函数可能具有未知的副作用。调用“printf”来打印中间结果的值将导致类似的行为。

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.

无论如何,除非您有非常好的理由(例如,调试),否则您通常不会关心这个问题,并且您应该信任编译器。

#1


4  

Yes, that is safe (at least in this case). You only use two "operators" there, the primary expression (something) and the binary something +/- something (additive).

是的,那是安全的(至少在这种情况下)。这里只使用两个“运算符”,即主表达式(某)和二进制的+/-某(添加剂)。

Section 1.9 Program execution (of C++0x N3092) states:

第1.9节程序执行(C+ 0x N3092)

Operators can be regrouped according to the usual mathematical rules only where the operators really are associative or commutative.

只有当运算符是结合的或可交换的时,运算符才能根据通常的数学规则重新分组。

In terms of the grouping, 5.1 Primary expressions states:

在分组方面,5.1主要表述如下:

A parenthesized expression is a primary expression whose type and value are identical to those of the enclosed expression. ... The parenthesized expression can be used in exactly the same contexts as those where the enclosed expression can be used, and with the same meaning, except as otherwise indicated.

圆括号表达式是一种主要表达式,其类型和值与所包含表达式的值相同。括号内的表达式可以在与所包含的表达式相同的上下文中使用,并且具有相同的含义,除非另有说明。

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:

对于加减法,第5.7节加算符有:

The additive operators + and - group left-to-right.

加性运算符+和-组从左到右。

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.

标准决定结果。如果编译器可以确定在不同的操作顺序下可以得到相同的结果,那么它可以重新排列它们。但无论这种情况是否发生,你都无法分辨出其中的差别。

#2


6  

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

您可能想看一下g++手册页:http://gcc.gnu.org/onlinedocs/gcc-4.6.1/gcc/Optimize-Options.html#优化选项。

Particularly -fassociative-math, -ffast-math and -ffloat-store

特别是-fassociative math, - ffastt -math和-ffloat-store

According to the g++ manual it will not reorder your expression unless you specifically request it.

根据g++手册,除非你特别要求,否则它不会重新排列你的表达式。

#3


5  

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.

这是一个非常合理的问题,因为英特尔的c++编译器(它被广泛使用)默认执行可以改变结果的优化。

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

看到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

#4


2  

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.

但是要注意FP寄存器的扩展精度。

Consult compiler documentation on how to ensure that FP values do not have extended precision.

请参阅编译器文档,了解如何确保FP值不具有扩展精度。

#5


0  

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.

也就是说,我要测试我使用的编译器。

#6


0  

Yes. The compiler will not change the order of your calculations within a block like that.

是的。编译器不会在这样的块中改变计算顺序。

#7


0  

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.

然而,它也保证这永远不会改变结果。c++遵循标准的操作顺序,所有的优化都保留这种行为。

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.

底线:别担心。编写c++代码,使其在数学上正确,并信任编译器。如果出了什么问题,问题几乎肯定不是编译器的问题。

#8


0  

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.

如果您确实需要,我认为您可以创建noinline函数no_reorder(float x) {return x;},然后用它代替括号。显然,这不是一个特别有效的解决方案。

#9


-1  

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.

按照其他答案你应该能够依靠编译器做正确的事情——大多数编译器允许您编译和检查汇编程序(使用- s gcc)——你可能想这样做,以确保你得到你期望的顺序操作。

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.

不同的优化级别(gcc - o _O2等)允许将代码重新(然而顺序代码这样不太可能受到影响),但我建议你应该那么隔离特定部分的代码到一个单独的文件,这样你可以控制优化级别的计算。

#10


-1  

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)

简单的回答是:编译器可能会改变计算的顺序,但它永远不会改变程序的行为(除非您的代码使用了未定义的行为的表达式: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.

但是,您仍然可以通过禁用所有编译器优化(选项“-O0”和gcc)来影响这种行为。如果您仍然需要编译器来优化其余的代码,您可以将这个函数放在单独的“。c,你可以用-O0编译。此外,您还可以使用一些技巧。例如,如果您在代码中插入了extern函数调用,编译器可能会认为重新排序代码是不安全的,因为该函数可能具有未知的副作用。调用“printf”来打印中间结果的值将导致类似的行为。

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.

无论如何,除非您有非常好的理由(例如,调试),否则您通常不会关心这个问题,并且您应该信任编译器。