On Friday, 13 September 2024 at 10:26:48 UTC, Richard (Rikki) Andrew Cattermole wrote:
>On 13/09/2024 10:20 PM, Quirin Schroll wrote:
>On Friday, 13 September 2024 at 10:02:25 UTC, Richard (Rikki) Andrew Cattermole wrote:
>On 13/09/2024 9:54 PM, Quirin Schroll wrote:
>The best thing about |pragma| is that it allows for additional arguments. For example, a |bool| to enable or disable it: |pragma(unlikely, false)| could be as if it’s not there. Great for meta-programming. For |pragma(likely)|, a numerical probability makes sense, too: |pragma(likely, 0)| is equivalent to |pragma(unlikely)| and a single |pragma(likely, value)| (with |value| > 0) is |pragma(likely)|.
We can do this with a UDA.
struct unlikely {
bool activate=true;
}
if (...) @unlikely(false) {
}
Compiler-recognized UDAs are actually a bad choice in this case. We’d need to change the grammar to allow them at this place in a very special and weird way and they’re harder to ignore.
Again, the advantage of a pragma is that it’s implementation defined and may end up not have any semantics at all, and this is already specified out. A compiler-recognized UDA is just the wrong tool for the job. The spec about pragmas is pretty clear about that and as a related feature, inline
is a pragma as well for this exact reason.
I just don’t understand why some people are adamant that those annotations should be attributes. To me, it makes not the least bit of sense.
All the arguments you are making for a pragma equally apply to a UDA.
I don’t see why. Compilers aren’t free to ignore @safe
and not issue an error if you violate its conditions, but every pragma is specified to be ignorable.
Except they have the benefit that you can override them, and redefine them if they are not supported.
You cannot do that with a pragma.
So there is a transition path for both old and new code with new compiler versions with the UDA's that are not present with pragmas and that is a major advantage for code maintenance and portability between compilers.
I fail to see why this is the case or even desirable. pragma(likely)
isn’t really likely to be in code bases anyway.
Generally speaking, if there are more than two branches, with two or more of them tagged |likely|, they can be given weights, that may be derived from abstract reasoning or profiling. That’s essentially what GCC has with |__builtin_expect_with_probability|, except that it’s with weights and not probabilities.
The way it works in D is if-else not if-elseif-else.
There is also switch
.
Walter has stated this elsewhere, that the order of declaration determines likelihood for cases.
Another case of relying on the choice of specific compilers. AFAICT, the whole point of likelihood annotations is that you can layout the code as you think it’s best to read, but sprinkle in some annotations that don’t hurt reading so that the compiler emits a better binary.
> > >So for something like this, you are swapping the assumption from one path to another.
I don't think we need probability support, just because of how the IR will be laid out to the backend.
GCC supports them, so I thought at least GDC could make use of them, LDC probably, too. DMD can just consider weights > 0 as equal and likely.
So does LLVM.
What I am questioning is the need to offer this in the language, as I don't think we can drive it. To drive it you need if-elseif-else, rather than if-else.
Well, we’re in circles here. I already mentioned switch
, and again, the reason for the annotation is that code can be laid out to be readable and idiomatic. Let’s say you have a big switch in a hot loop. You profiled and now the data tells you how likely each branch was. What would you prefer? Reordering the branches by likelihood, leading to a diff that’s basically impossible to understand or even vet that it’s just a reordering, or the pure addition of likelihood annotations, for which in the diff it’s absolutely clear nothing else changes. And if there’s a fallthrough, you have to jump to the right case now.
You’re arguing as if the compiler couldn’t recognize else if
as a pattern.
The reason a compiler optimizes the non-branch or switch cases by ordering (except if it has another clear indication of what’s a cold path, e.g. a thrown exception) is because absent any information, it has to do something and be deterministic. For most cases, it’s fine. Optimization hints are an expert tool.