Live data from Hacker News

Optimizations Enabled by -ffast-Math

kristerw.github.io

41–50 of 117 posts

Re: Optimizations Enabled by -ffast-Math

#41
post #11

I found the following note for -ffinite-math-only and -fno-signed-zeros quite worrying: The program may behave in strange ways (such as not evaluating either the true or false part of an if-statement) if calculations produce Inf, NaN, or -0.0 when these flags are used. I always thought that -ffast-math was telling the compiler. "I do not care about floating point standards compliance, and I do not rely on it. So opti…

> I always thought that -ffast-math was telling the compiler. "I do not care about floating point standards compliance, ... So optimize things that break the standard". But instead it seems like this also implies a promise to the compiler [that] you will not produce Inf, NaN, or -0.0.

I see it as both, as with many such flags¹n though as you say it does put the onus on the developer to avoid inputs the optimisation may cause problems for. You are saying “I promise that I'm being careful not to do things that will be affected by these parts of the standard, or won't blame you for undefined behaviour if I do, so you can skip those rules if it helps you speed things up”.

[1] like those that enable optimisations which can cause significant breakage if pointer aliasing is happening

Re: Optimizations Enabled by -ffast-Math

#42
post #2

The question is how to enable all this in Rust... Right now there's no simple way to just tell Rust to be fast & loose with floats.

I don't know if most Rust programmers would be happy with any fast and loose features making it into the official Rust compiler.

Besides, algorithms that benefit from -ffast-math can be implemented in C and with Rust bindings automatically generated.

This solution isn't exactly "simple", but it could help projects keep track of the expectations of correctness between different algorithm implementations.

Re: Optimizations Enabled by -ffast-Math

#43

Earlier quoted context omitted.

Generally if you're seeing a NaN/Inf something has gone wrong, It's very difficult to gracefully recover from and if you tried I think you would lose both sanity and performance! Regarding performance, the cost of a real division is about 3-4 orders worse performance than an if statement that is very consistent, but the usual way is to have fast/safe versions of functions, where you need performance and can deduce if…

> Generally if you're seeing a NaN/Inf something has gone wrong That’s a bold claim.

Can you think of a function where the input is valid, the output is NaN and nothing has gone wrong in the process?

I can't think of any, haven't experienced any, not heard of any examples of it, so you're welcome to break my ignorance on the subject.

Re: Optimizations Enabled by -ffast-Math

#44
post #7

Earlier quoted context omitted.

Denormal floats are not a purpose, they are not used intentionally. When the CPU generates denormal floats on underflow, that ensures that underflow does not matter, because the errors remain the same as at any other floating-point operation. Without denormal floats, underflow must be an exception condition that must be handled somehow by the program, because otherwise the computation errors can be much higher than e…

Flushing denormals to zero only matters if your calculations are already running into the lower end of floating-point exponents (and even with denormals, if they're doing that, they're going to run into lost precision anyway sooner or later). The useful thing denormals do is make the loss of precision at that point gradual, instead of sudden. But you're still losing precision, and a few orders of magnitude later you'…

Your arguments are correct, but the conclusion does not result from them.

If we assume that underflows happen in your program and this, as you say, is a sign that greater problems will be caused by that, then you must not enable flush-to-zero, but you must enable trap-on-underflow, to see where underflows happen and to investigate the reason and maybe rearrange your formulas to avoid the too small results.

Flush-to-zero may sometimes lead to crashes, when it becomes obvious that something is wrong, but more often you just get some results with large errors that are difficult to distinguish from good results.

Opinions obviously vary, but I have never seen any good use case for flush-to-zero.

Underflows are either so rare that their performance does not matter, or if they are so frequent that flush-to-zero will increase the speed, then your code has a problem and the formulas should be restructured, which will both increase the speed and eliminate the loss of precision.

Re: Optimizations Enabled by -ffast-Math

#45

Earlier quoted context omitted.

Generally if you're seeing a NaN/Inf something has gone wrong, It's very difficult to gracefully recover from and if you tried I think you would lose both sanity and performance! Regarding performance, the cost of a real division is about 3-4 orders worse performance than an if statement that is very consistent, but the usual way is to have fast/safe versions of functions, where you need performance and can deduce if…

> Generally if you're seeing a NaN/Inf something has gone wrong That’s a bold claim.

What makes it a bold claim?

Re: Optimizations Enabled by -ffast-Math

#46

Earlier quoted context omitted.

> Generally if you're seeing a NaN/Inf something has gone wrong That’s a bold claim.

Can you think of a function where the input is valid, the output is NaN and nothing has gone wrong in the process? I can't think of any, haven't experienced any, not heard of any examples of it, so you're welcome to break my ignorance on the subject.

Do you count under/overflow resulting in Inf as 'something has gone wrong'? If so, why would gracefully recovering be hard?

Re: Optimizations Enabled by -ffast-Math

#47

Earlier quoted context omitted.

But couldn’t the compiler optimize out the check because you’re guaranteeing to never divide by 0, so logically count would never be 0. Akin to undefined behavior almost.

This test can not be optimized to always true: if (count > 0) total += unit_price(volume, count); Are you confusing it with the opposite scenario: total += unit_price(volume, count); if (count == 0) printf("oops\n"); That test might be optimized out because divide by zero is undefined behavior.

I probably am confusing them. I don’t program in C or C++, so I’m not aware of all the “gotchas”, and can easily confuse them (as shown here). Not to mention that compilers have been getting more aggressive in optimizing undefined behavior.

Re: Optimizations Enabled by -ffast-Math

#48
post #32

Earlier quoted context omitted.

with gcc you can also use #pragma gcc optimize or __attribute__(optimize(...)) for a similar effect. It is not 100% bug free (at least it didn't use to) and often it prevents inlining a function into another having different optimization levels (so in practice its use has to be coarse grained).

This pragma doesn't quite work for -ffast-math https://gcc.godbolt.org/z/voMK7x7hG Try it with and without the pragma, and adding -ffast-math to the compiler command line. It seems that with the pragma sqrt(x) * sqrt(x) becomes sqrt(x*x), but with the command line version it is simplified to just x.

That's very interesting. The pragma does indeed do a lot of optimizations compared to no-pragma (for example it doesn't call sqrtf at all), but the last simplification is only done with the global flag set. I wonder if it is a missed optimization or if there is a reason for that.

edit: well with pragma fast-math it appears it is simply assuming finite math and x>0 thus skipping the call to the sqrtf as there are not going to be any errors, basically only saving a jmp. Using pragma finite-math-only and an explicit check are enough to trigger the optimization (but passing finite-math as a command line is not).

Generally it seems that the various math flags behave slightly differently in the pragma/attribute.

Re: Optimizations Enabled by -ffast-Math

#49

Earlier quoted context omitted.

As the compiler assumes you wont produce such values, wouldnt it also optimise those range checks away?

At least GCC is I believe fairly buggy in that regard since isnan is optimised out, thus you cannot check if say an incoming value read from the network is a NaN. There's some lengthy discussion about it on the big tracker iirc..

I was misremembering, was thinking about this recent convo on LLVM mailinglist: https://lists.llvm.org/pipermail/llvm-dev/2021-September/152...

Re: Optimizations Enabled by -ffast-Math

#50

Earlier quoted context omitted.

> Generally if you're seeing a NaN/Inf something has gone wrong That’s a bold claim.

Can you think of a function where the input is valid, the output is NaN and nothing has gone wrong in the process? I can't think of any, haven't experienced any, not heard of any examples of it, so you're welcome to break my ignorance on the subject.

Calculating the average value for some observation in a time bucket, where some buckets may have no observation (resulting in 0/0=nan), finally taking some kind of summary of these ignoring nan values (there is a whole library of functions for this in numpy for example).

NaN and Inf are extremely valuable and useful encodings to represent real things. Particularly signed infinity is wonderful, as functions like exp behave correctly for inf (eg. exp(-inf)=0).

Post reply on HN