# Switch expression performance: part 2

Previously we looked at the performance of simple switch statements and expressions. Today we will consider switch expressions with when clauses.

This example shows a simple letter grade calculator as might be defined for a US high school:

```    public static class MyGrade
{
public static char Calc0(double pct)
{
if (pct < 0.6d)
{
return 'F';
}
else if (pct < 0.7d)
{
return 'D';
}
else if (pct < 0.8d)
{
return 'C';
}
else if (pct < 0.9d)
{
return 'B';
}
else
{
return 'A';
}
}

public static char Calc1(double pct)
{
return pct switch
{
_ when pct < 0.6d => 'F',
_ when pct < 0.7d => 'D',
_ when pct < 0.8d => 'C',
_ when pct < 0.9d => 'B',
_ => 'A',
};
}
}
```

The first method shows the legacy approach with successive if/else statements. Yawn. The second method replaces all this boilerplate with a simple switch expression. It’s much nicer to read, for sure. But will it result in any unexpected overhead? To the benchmark!

```    [SimpleJob(RuntimeMoniker.NetCoreApp31)]
[MemoryDiagnoser]
public class SwitchBenchmark2
{
[Benchmark]
public int IfElse()
{
int sum = 0;
return sum;
}

[Benchmark]
public int Switch()
{
int sum = 0;
return sum;
}
}
```

The result:

```| Method |     Mean |    Error |   StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated |
|------- |---------:|---------:|---------:|------:|------:|------:|----------:|
| IfElse | 19.84 ns | 0.123 ns | 0.109 ns |     - |     - |     - |         - |
| Switch | 19.48 ns | 0.104 ns | 0.087 ns |     - |     - |     - |         - |
```

Luckily the performance is essentially the same. For fun, let’s peek at the IL anyway:

```.method public hidebysig static
char Calc0 (
float64 pct
) cil managed
{
// Method begins at RVA 0x2050
// Code size 63 (0x3f)
.maxstack 8

// if (pct < 0.6)
IL_0000: ldarg.0
IL_0001: ldc.r8 0.6
IL_000a: bge.un.s IL_000f

// return 'F';
IL_000c: ldc.i4.s 70
// (no C# code)
IL_000e: ret

// if (pct < 0.7)
IL_000f: ldarg.0
IL_0010: ldc.r8 0.7
IL_0019: bge.un.s IL_001e

// return 'D';
IL_001b: ldc.i4.s 68
// (no C# code)
IL_001d: ret

// if (pct < 0.8)
IL_001e: ldarg.0
IL_001f: ldc.r8 0.8
IL_0028: bge.un.s IL_002d

// return 'C';
IL_002a: ldc.i4.s 67
// (no C# code)
IL_002c: ret

// if (pct < 0.9)
IL_002d: ldarg.0
IL_002e: ldc.r8 0.9
IL_0037: bge.un.s IL_003c

// return 'B';
IL_0039: ldc.i4.s 66
// (no C# code)
IL_003b: ret

// return 'A';
IL_003c: ldc.i4.s 65
// (no C# code)
IL_003e: ret
} // end of method MyGrade::Calc0

.method public hidebysig static
char Calc1 (
float64 pct
) cil managed
{
// Method begins at RVA 0x2090
// Code size 73 (0x49)
.maxstack 2
.locals init (
[0] char
)

// if (pct < 0.6)
IL_0000: ldarg.0
IL_0001: ldc.r8 0.6
IL_000a: bge.un.s IL_0011

// return 'F';
IL_000c: ldc.i4.s 70
IL_000e: stloc.0
// (no C# code)
IL_000f: br.s IL_0047

// if (pct < 0.7)
IL_0011: ldarg.0
IL_0012: ldc.r8 0.7
IL_001b: bge.un.s IL_0022

// return 'D';
IL_001d: ldc.i4.s 68
IL_001f: stloc.0
// (no C# code)
IL_0020: br.s IL_0047

// if (pct < 0.8)
IL_0022: ldarg.0
IL_0023: ldc.r8 0.8
IL_002c: bge.un.s IL_0033

// return 'C';
IL_002e: ldc.i4.s 67
IL_0030: stloc.0
// (no C# code)
IL_0031: br.s IL_0047

// if (pct < 0.9)
IL_0033: ldarg.0
IL_0034: ldc.r8 0.9
IL_003d: bge.un.s IL_0044

// return 'B';
IL_003f: ldc.i4.s 66
IL_0041: stloc.0
// (no C# code)
IL_0042: br.s IL_0047

// return 'A';
IL_0044: ldc.i4.s 65
IL_0046: stloc.0

// (no C# code)
IL_0047: ldloc.0
IL_0048: ret
} // end of method MyGrade::Calc1
```

Funny enough, the switch expression code is again larger with an extra local, while the if/else formulation takes advantage of early returns. In the case of this microbenchmark, however, it doesn’t seem to make a difference in speed.

Summing up, switch expressions are great for conciseness and readability. Their runtime cost is not bad either, though they could perhaps benefit from better compiler optimization.