- 
                Notifications
    You must be signed in to change notification settings 
- Fork 5.2k
Description
Description
Using the ternary operator with constants creates a lot of unnecessary branches and jumps. It can be factored into a common base and based on the boolean result, the difference can be multiplied with the condition state and then added -- or a "-1" mask (negated state) on the difference can be used.
clang, gcc, msvc all do that.
public int Value(bool cond) => cond ? c_ConstA : c_ConstB;It's a common code pattern to return 2 different constants based on a flag, on a condition, etc. Like makeUpper ? 'A' : 'a'
public int Value(bool cond) => c_ConstA + cond * (c_ConstB - c_ConstA);public int Value(bool cond) => c_ConstA + (-cond & (c_ConstB - c_ConstA));Note: C# cannot easily convert a boolean into an integer (0/1) but the JIT will have no issue with that.
Configuration
.NET 6.0.0 (6.0.21.52210)
Regression?
No
Data
Analysis
    [Benchmark]
    public int A() => Cond ? 171 : 239;
    [Benchmark]
    public int B() => 171 + CondInt * (239 - 171);
    volatile bool Cond;
    volatile int  CondInt; // bool as int as there is no friction-free C# conversion.NET 6.0.0 (6.0.21.52210), X64 RyuJIT
; Benchmark+Bench.A()
       7FFB738AD610 cmp       byte ptr [rcx+18],0
       7FFB738AD614 jne       short M00_L00
       7FFB738AD616 mov       eax,0EF
       7FFB738AD61B ret
M00_L00:
       7FFB738AD61C mov       eax,0AB
       7FFB738AD621 ret
; Total bytes of code 18.NET 6.0.0 (6.0.21.52210), X64 RyuJIT
; Benchmark+Bench.B()
       7FFB7389C4E0 imul      eax,[rcx+14],44
       7FFB7389C4E4 add       eax,0AB
       7FFB7389C4E9 ret
; Total bytes of code 10I did some benchmarking, but added a 1000-loop to reduce the call-frame jitter, and the difference is significant. And that doesn't even include branch misprediction which real-world code will have at around 50%. Here, misprediction is 0% as the code is too artificial and predictable. Nonetheless, it's twice as fast and with bad branch prediction it will be an order of magnitude when the 18-cycle penalty has an effect.
    [Benchmark]
    public bool A()
    {
      for (var nn = 0 ; nn < 1000 ; ++nn) Value = Cond ? 171 : 239;
      return true;
    }
    [Benchmark]
    public bool B()
    {
      for (var nn = 0 ; nn < 1000 ; ++nn) Value = 171 + CondI * (239 - 171);
      return true;
    }
    volatile int  Value;
    volatile bool Cond;
    volatile int  CondI;| Method |     Mean |   Error |  StdDev | Code Size |
|------- |---------:|--------:|--------:|----------:|
|      A | 756.3 ns | 2.37 ns | 2.22 ns |      41 B |
|      B | 434.1 ns | 0.26 ns | 0.24 ns |      30 B |