Right. The way they are implemented, -ffast-math
is less safe than -funsafe-math-optimizations
. It’s not limited to the macros they set. The biggest difference is that -ffast-math
sets the nnan
and ninf
fast-math flags and associated function and parameter attributes, while -funsafe-math-optimizations
does not. More explicitly, with -ffast-math
something like isnan(x)
or isinf(x)
is always folded to false
whereas with -funsafe-math-optimizations
it is not.
It seems to me that these comments show that we don’t have a common understanding amongst ourselves as to what the “-Ofast” option means and how it is used. I tend to agree with @JonChesterfield’s characterization of the option as meaning “make it faster and wronger please” and while I’m sure there are people who use this option without knowing that it means that, the people who use it knowing exactly what it means probably don’t see “make it faster and wronger” as a bad thing.
Basically, what the option says is, “Take whatever liberties the compiler thinks are reasonable, even disregarding applicable standards, to make the program run as fast as possible.” The key thing here is that the user of this option is explicitly giving a sort of power of attorney to the compiler to decide which rules it wants to break. This is the key to why this option is more useful than just stringing together the controlling options for all of the behaviors it enables. If someone implements a new optimization tomorrow that provides big performance gains by ignoring some aspect of a standard, we could enable it under -Ofast
and the daring users who compile with -Ofast
would get it. Would it break their code? Maybe. But that’s exactly what they signed up for.
If we deprecate the option and as @Endill suggests use that as justification to close bugs related to the behavior of the option, we’ve taken away the channel by which users can say “maybe you’re getting a little too aggressive with this one.”
As for the claim that -Ofast is to whatever extent “incompatible between Clang and GCC,” I’m not sure this is the right way to view it. If the option means “break whatever rules you need to to make my program fast,” then compatibility is a murky question. Do we have to break all the rules GCC breaks? Definitely not. Is it OK to break rules that they don’t? That’s a little less clear, but I think it is.
On this last point, let me observe that GCC respects floating-point exceptions at -O2
(without any additional fast-math flags), and clang does not. Does this mean that -O2
is incompatible between GCC and clang? I don’t think so. It just means that we made different choices within the degrees of freedom available to us.
Passionately agree with the above. A major gcc option (I think it can be agreed this is a major option given the input on the RFC) should exist in Clang. I don’t think it should viewed as runtime compatible because it’s simply not possible to get identical answers in all situations between any compiler in which FP optimization is enabled, just as writing a + b + c
may not produce the same result as a user writing c + b + a
within the same compiler when FP optimization is enabled.
The compiler is a tool that a software engineer needs to learn how to use properly if they want to do their job/hobby effectively. At some point the user has to learn what FP optimization means and (as said several times on this thread) deprecating -Ofast
doesn’t do anything to help that. What it does – as suggested – is give you the ability to close bugs. From what I can tell that seems to have been the prime motivator from the start yet, those can be solved with a link to documentation that would educate the user (and maybe even include a brief primer/links on FP optimization) and then close the bug as “not a bug”.
Like it or not, -Ofast
is the way to achieve peak performance across most general purpose (LLVM based and non-LLVM based) compilers. To me, it’s pretty easy to just define -Ofast
is THE option to enable peak performance at a (potential) cost to FP accuracy. There are countless existing docs/presentations/papers that suggest using the option and companies have spent years/$$$$$$ telling their users to use -Ofast
. To undo that is a mistake.
But this is precisely the problem! People spread around that one should use -Ofast
and so others take it to be a thing they should use, without considering that it might not actually be a good idea, because they don’t actually understand what it does. Having the ability to opt into FP-breaking optimisations because they don’t matter for you is totally valid, but having it be so easy to shoot your foot off with something that looks to be like a good idea (why wouldn’t you want to turn on an optimisation level that’s called “fast”, that’s why you turn on optimisations!) is terrible design that hurts users. Just because GCC, and possibly other compilers, has, and refuses to remove, dangerous UX doesn’t mean we need to replicate it.
I feel we are going in circles, but about this:
I would like to ask again and again why this means we need to deprecate with the intend to remove the option? Why can’t we emit a warning/diagnostic? I am really hoping for an answer here, which is why I split this off in:
Forking the conversation means that nobody involved in this RFC is alerted to the continued discussion, which makes it much harder for me to judge what community sentiment is (it leads to questions of “person X said Y on the other thread but they’ve not weighed in on this thread, why?”). I understand why you forked the thread, but that’s why I’m engaging on it here rather than there.
There are two separate topics here: how decisions are made, and what do we want to do about -Ofast
.
In terms of how decisions are made: until we have a better process for decision-making, we’re stuck with the best effort we have today. So ultimately, if you’re not happy with how decisions are made in general, the appropriate avenue to make your concerns known is via the governance proposal (or similar). The way decisions are made in Clang is unfortunately not documented, but it is not ad hoc either; I follow the same model used by the standards committees for lack of a better approach. That model is roughly: someone proposes a change to the status quo, we debate the merits of the changes, eventually we decide whether the changes have consensus or not, and once we establish consensus that sets a new status quo unless we get new information or a new proposal. Consensus is not formulaic, it’s a read of the room based on what comments are made, how often they’re made, who is making the comments, etc. Calling into question whether there is or is not consensus is not useful. Doing so a full month after consensus was called and people were asked explicitly to raise disagreement makes an already unpleasant process worse and it contributes to burnout. I ask to please stop that practice – instead, once a decision has been reached, it is far more useful to bring new information or a new proposal than to argue whether there is or is not consensus on a previous decision that’s been explicitly communicated.
Regarding -Ofast
specifically, here is the current state of things as I see them:
- The status quo is that
-Ofast
is deprecated - Since that decision was reached, we’ve discovered new information that warrants reflecting on our decision:
-Ofast
is not and never has been compatible with GCC’s option of the same spelling-Ofast
does more than we realized it does; for example, it also sometimes enables-fstrict-aliasing
-Ofast
overwrites previous explicitly spelled flags; for example-Ofast -fno-strict-aliasing
is silently different than-fno-strict-aliasing -Ofast
- the GCC community has no appetite to deprecate or remove
-Ofast
- We branch tomorrow and expect a release in September
- There is no behavioral change beyond issuing a diagnostic, so this is not causing regressions and there’s not a strong need for an immediate revert; reverting or making additional changes on the branch is unlikely to be onerous (any changes including a full revert are small and isolated)
- Some people who were opposed to the direction initially remain opposed to the status quo, but would like to reopen the decision in light of the new information we’ve found.
Given all the new information, reopening the discussion is totally appropriate at this time, with the goal of finding out if there is consensus to either remove the deprecation entirely or make other changes.
I’d like to propose a way forward (personal opinion, not code owner statement):
It seems like there’s still very strong agreement that -Ofast
has poor usability. Where I see most of the disagreement coming from is what “deprecated” means and whether -Ofast
should ever be removed given that it exists in other compilers and has existed in Clang for quite a while. Stepping back, the goal of the original proposal is: tell users about the fact that this optimization option impacts the correctness of their code and discourage new code from using it. FWIW, “this is problematic, discourage new code from using it” is what I’ve always understood “deprecated” to mean, so I still remain perfectly comfortable with using the term “deprecated”. However, if others find that to be too loaded of a term, we could reword to say “discouraged” or something along those lines. In terms of whether -Ofast
is removed or not and on what schedule, I would be fine with the option never being removed so long as it’s use is strongly discouraged via a diagnostic (to me, discouraging users via documentation is inadequate to solve the usability issues with the option). I do not find any argument about GCC compatibility compelling because the options are not compatible; in fact, I think it does users a disservice to imply there’s some sort of relationship between the GCC behavior and the Clang behavior. We don’t have to retain the same usability mistakes as other compilers.
Thanks @AaronBallman, this is an excellent summary.
To me deprecation means eventual removal, which is also the impression I got from this RFC:
The deprecation period should be lengthy enough for users to migrate before we remove the flag; two years seems to be a suggestion with support
This would be the ideal solution to me:
I would be fine with the option never being removed so long as it’s use is strongly discouraged via a diagnostic (to me, discouraging users via documentation is inadequate to solve the usability issues with the option).
Sorry for forking off a separate thread, I thought it would help to separate things, but what you mentioned above is what I am trying to say here in previous messages and in that RFC. I think we achieve everything we want if we emit a clear warning/diagnostic: give clear guidance (or discourage its usage) without removing an existing option. So, to me, this would be the ideal outcome.
Small nit:
There is no behavioral change beyond issuing a diagnostic, so this is not causing regressions
If you build with -Werror there is a change in behaviour?
-Werror
makes any enabled-by-default diagnostic a change in behavior. Despite that, we don’t consider new diagnostics a breaking change that requires an RFC.
If you build with -Werror and use -Ofast your build will start failing with this change which wasn’t happening before. That’s a regression and will be a change in behaviour of the compiler to some folks.
But as I said, I am happy with a diagnostic and the behavioural change if we don’t deprecate/remove the option. So, to be very explicit, if we can just remove the word “deprecated” from the patch that introduced it then I think we are nearly there.
Yeah, the consensus position was originally to remove, hopefully sometime after two years, so your impression was not wrong! What I don’t think was communicated very well was: removal requires its own RFC and the result of that RFC can certainly be “not removing, just perpetually deprecated”.
Excellent, thank you! Just to make sure though, it sounds like you’re comfortable with the deprecation but only so long as there’s sufficiently clear communication that deprecation means “discourage use of the feature, it might be removed in the future”. Do I have that right?
If so, then I think what we should do is:
- Update the release note to say:
- The ``-Ofast`` command-line option has been deprecated. This option both
enables the ``-O3`` optimization-level, as well as enabling <ins>other
options, including</ins> non-standard ``-ffast-math`` behaviors. As such,
it is somewhat misleading as an "optimization level". Users are
advised to switch to ``-O3 -ffast-math <ins>-fstrict-aliasing</ins>``
<del>if the use of non-standard math behavior is intended</del>
<ins>to retain the previous behavior</ins>, and ``-O3`` otherwise.
**Note**: there is no firm date for removal of the option; its
use is discouraged and the option may or may not be removed
in the future.</ins> See
`RFC <https://discourse.llvm.org/t/rfc-deprecate-ofast/78687>`_
for details.
- Update the diagnostic message to say:
"argument '-Ofast' is deprecated; use '-O3 -ffast-math <ins>-fstrict-aliasing</ins>' "
"for the same behavior, or '-O3' to enable only conforming optimizations">,
- Remove the diagnostic for flang in light of [RFC] Deprecate Ofast in Flang; this RFC was only ever about Clang, the Flang community should be free to make their own decisions in their own time.
- Write some documentation for our optimization levels, and include more information in the entry for
-Ofast
. (Because we have no existing documentation for any optimization levels, this is a “nice to have”, not a requirement to be completed before we can ship.)
WDYT?
No worries at all! I was actually thinking over the weekend about whether to fork the discussion or not, so you weren’t alone in that idea.
Sort of, but not a change in behavior we are willing to gate decisions on. Otherwise, it would be incredibly onerous to ever introduce a new diagnostic because any new diagnostic, even ones that are off-by-default, could potentially cause the program to not compile. Users who use -Werror
are effectively opting in to having compiler upgrades be more disruptive but with the benefit of trivially discovering new warning behaviors after the upgrade.
FTR, Sony does this and accepts it as a cost of doing business. I can’t remember a command-line option ever being the problem before, though. (I am 80% sure we don’t use -Ofast
.)
Currently, Clang emits this diagnostic under its own warning flag (-Wdeprecated-ofast
) so folks can opt out of this warning while still getting all the other deprecation warnings, so hopefully it’s easy enough for users in that situation to address it: Compiler Explorer
A user got the warning on their nightly testing today. They tried applying the change suggested by the warning, and saw performance regressions on both clang and gcc (they were handling both with the same set of flags =/).
Is there an ETA to update the warning message to at least accurately describe how to recover -Ofast
behavior correctly with clang? That is, include the -fstrict-aliasing
and any other flags and macros that -Ofast
was defining in the warning. Do we have tests to keep the warning in sync with the flags?
IIUC, the recommendation for users should be to “stop using -Ofast
with clang only, continue using -Ofast
with gcc”, since in particular the sets of flags described by the clang warning are “replacement flags for clang”, but for obvious reasons, they do not cover everything that GCC -Ofast
covers, and it seems that at least for now GCC’s preference is for users to continue using -Ofast
.
Yes, exactly right, thank you. I agree and am happy with the general messages here.
One question: do you think we can change “deprecated” into “discouraged” in the diagnostic and release notes? My concern is that “deprecated” is a strong bias towards removal when we discuss this next time. If we keep it as discouraged, we can discuss more from first principles if we want to keep it “discouraged” or promote it to “deprecated”, and by that time we should also have more data and experience.
Again, with the note on deprecated/discouraged, I like the messages, thanks for your help with this @AaronBallman.
At the risk of muddying the waters even further, perhaps we should reconsider this behavior (separately from the current discussion). Looking at -fno-fast-math, I see that for GCC both -fno-fast-math -Ofast
and -Ofast -fno-fast-math
leave fast-math off. In clang, the behavior is different (and less predicatable):
-fno-fast-math -Ofast
enables fast-math-Ofast -fno-fast-math
disables fast-math-fno-fast-math -Ofast -O2
disables fast-math-ffast-math -Ofast -O2
enables fast-math-Ofast -O2
disables fast-math
The reasoning that leads to this is:
- If the prevailing optimization-level is Ofast, it is treated as an alias for
-ffast-math
when it is encountered. - If the prevailing optimization-level is not Ofast, it is ignored when encountered.
We have similar behavior for strict aliasing – -fno-strict-aliasiang -Ofast
behaves differently than -fno-strict-aliasiang -Ofast -O2
I know I’ve said above that I don’t think we need to strictly match the GCC behavior for -Ofast, but the rules we’re applying are not intuitive.
Agreed. This discussion, plus ones we’ve had before around -ffast-math
make me think we should find a way to encode relationships between driver flags such that we can automatically diagnose situations where a later omnibus flag overrides an earlier flag, or something else to help the user discover what their command line actually does.
It’s starting to be a question of whether we can describe the replacement set of flags succinctly enough to be a reasonable diagnostic message. I was really hoping we could avoid telling users to read the docs for details, but the replacement isn’t trivial depending on how you structured your command line flags.
Btw, one thing to check regarding the performance regression is whether the replacement flags are in the same location in the overall command line string as -Ofast
was; it’s possible that -Ofast
was silently overriding earlier options.