{"id":5762,"date":"2020-05-26T07:00:18","date_gmt":"2020-05-26T14:00:18","guid":{"rendered":"http:\/\/writeasync.net\/?p=5762"},"modified":"2020-05-24T16:13:10","modified_gmt":"2020-05-24T23:13:10","slug":"switch-expression-performance-part-2","status":"publish","type":"post","link":"http:\/\/writeasync.net\/?p=5762","title":{"rendered":"Switch expression performance: part 2"},"content":{"rendered":"<p>Previously we looked at the <a href=\"http:\/\/writeasync.net\/?p=5758\">performance of simple switch statements and expressions<\/a>. Today we will consider switch expressions with when clauses.<\/p>\n<p>This example shows a simple letter grade calculator as might be defined for a US high school:<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\n    public static class MyGrade\r\n    {\r\n        public static char Calc0(double pct)\r\n        {\r\n            if (pct &lt; 0.6d)\r\n            {\r\n                return 'F';\r\n            }\r\n            else if (pct &lt; 0.7d)\r\n            {\r\n                return 'D';\r\n            }\r\n            else if (pct &lt; 0.8d)\r\n            {\r\n                return 'C';\r\n            }\r\n            else if (pct &lt; 0.9d)\r\n            {\r\n                return 'B';\r\n            }\r\n            else\r\n            {\r\n                return 'A';\r\n            }\r\n        }\r\n\r\n        public static char Calc1(double pct)\r\n        {\r\n            return pct switch\r\n            {\r\n                _ when pct &lt; 0.6d =&gt; 'F',\r\n                _ when pct &lt; 0.7d =&gt; 'D',\r\n                _ when pct &lt; 0.8d =&gt; 'C',\r\n                _ when pct &lt; 0.9d =&gt; 'B',\r\n                _ =&gt; 'A',\r\n            };\r\n        }\r\n    }\r\n<\/pre>\n<p>The first method shows the legacy approach with successive if\/else statements. <em>Yawn.<\/em> The second method replaces all this boilerplate with a simple switch expression. It&#8217;s much nicer to read, for sure. But will it result in any unexpected overhead? To the benchmark!<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\n    &#x5B;SimpleJob(RuntimeMoniker.NetCoreApp31)]\r\n    &#x5B;MemoryDiagnoser]\r\n    public class SwitchBenchmark2\r\n    {\r\n        &#x5B;Benchmark]\r\n        public int IfElse()\r\n        {\r\n            int sum = 0;\r\n            sum += MyGrade.Calc0(1.2d);\r\n            sum += MyGrade.Calc0(0.94d);\r\n            sum += MyGrade.Calc0(0.81d);\r\n            sum += MyGrade.Calc0(0.7d);\r\n            sum += MyGrade.Calc0(-0.1d);\r\n            return sum;\r\n        }\r\n\r\n        &#x5B;Benchmark]\r\n        public int Switch()\r\n        {\r\n            int sum = 0;\r\n            sum += MyGrade.Calc1(1.2d);\r\n            sum += MyGrade.Calc1(0.94d);\r\n            sum += MyGrade.Calc1(0.81d);\r\n            sum += MyGrade.Calc1(0.7d);\r\n            sum += MyGrade.Calc1(-0.1d);\r\n            return sum;\r\n        }\r\n    }\r\n<\/pre>\n<p>The result:<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\n| Method |     Mean |    Error |   StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated |\r\n|------- |---------:|---------:|---------:|------:|------:|------:|----------:|\r\n| IfElse | 19.84 ns | 0.123 ns | 0.109 ns |     - |     - |     - |         - |\r\n| Switch | 19.48 ns | 0.104 ns | 0.087 ns |     - |     - |     - |         - |\r\n<\/pre>\n<p>Luckily the performance is essentially the same. For fun, let&#8217;s peek at the IL anyway:<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\n.method public hidebysig static \r\n    char Calc0 (\r\n        float64 pct\r\n    ) cil managed \r\n{\r\n    \/\/ Method begins at RVA 0x2050\r\n    \/\/ Code size 63 (0x3f)\r\n    .maxstack 8\r\n\r\n    \/\/ if (pct &lt; 0.6)\r\n    IL_0000: ldarg.0\r\n    IL_0001: ldc.r8 0.6\r\n    IL_000a: bge.un.s IL_000f\r\n\r\n    \/\/ return 'F';\r\n    IL_000c: ldc.i4.s 70\r\n    \/\/ (no C# code)\r\n    IL_000e: ret\r\n\r\n    \/\/ if (pct &lt; 0.7)\r\n    IL_000f: ldarg.0\r\n    IL_0010: ldc.r8 0.7\r\n    IL_0019: bge.un.s IL_001e\r\n\r\n    \/\/ return 'D';\r\n    IL_001b: ldc.i4.s 68\r\n    \/\/ (no C# code)\r\n    IL_001d: ret\r\n\r\n    \/\/ if (pct &lt; 0.8)\r\n    IL_001e: ldarg.0\r\n    IL_001f: ldc.r8 0.8\r\n    IL_0028: bge.un.s IL_002d\r\n\r\n    \/\/ return 'C';\r\n    IL_002a: ldc.i4.s 67\r\n    \/\/ (no C# code)\r\n    IL_002c: ret\r\n\r\n    \/\/ if (pct &lt; 0.9)\r\n    IL_002d: ldarg.0\r\n    IL_002e: ldc.r8 0.9\r\n    IL_0037: bge.un.s IL_003c\r\n\r\n    \/\/ return 'B';\r\n    IL_0039: ldc.i4.s 66\r\n    \/\/ (no C# code)\r\n    IL_003b: ret\r\n\r\n    \/\/ return 'A';\r\n    IL_003c: ldc.i4.s 65\r\n    \/\/ (no C# code)\r\n    IL_003e: ret\r\n} \/\/ end of method MyGrade::Calc0\r\n\r\n.method public hidebysig static \r\n    char Calc1 (\r\n        float64 pct\r\n    ) cil managed \r\n{\r\n    \/\/ Method begins at RVA 0x2090\r\n    \/\/ Code size 73 (0x49)\r\n    .maxstack 2\r\n    .locals init (\r\n        &#x5B;0] char\r\n    )\r\n\r\n    \/\/ if (pct &lt; 0.6)\r\n    IL_0000: ldarg.0\r\n    IL_0001: ldc.r8 0.6\r\n    IL_000a: bge.un.s IL_0011\r\n\r\n    \/\/ return 'F';\r\n    IL_000c: ldc.i4.s 70\r\n    IL_000e: stloc.0\r\n    \/\/ (no C# code)\r\n    IL_000f: br.s IL_0047\r\n\r\n    \/\/ if (pct &lt; 0.7)\r\n    IL_0011: ldarg.0\r\n    IL_0012: ldc.r8 0.7\r\n    IL_001b: bge.un.s IL_0022\r\n\r\n    \/\/ return 'D';\r\n    IL_001d: ldc.i4.s 68\r\n    IL_001f: stloc.0\r\n    \/\/ (no C# code)\r\n    IL_0020: br.s IL_0047\r\n\r\n    \/\/ if (pct &lt; 0.8)\r\n    IL_0022: ldarg.0\r\n    IL_0023: ldc.r8 0.8\r\n    IL_002c: bge.un.s IL_0033\r\n\r\n    \/\/ return 'C';\r\n    IL_002e: ldc.i4.s 67\r\n    IL_0030: stloc.0\r\n    \/\/ (no C# code)\r\n    IL_0031: br.s IL_0047\r\n\r\n    \/\/ if (pct &lt; 0.9)\r\n    IL_0033: ldarg.0\r\n    IL_0034: ldc.r8 0.9\r\n    IL_003d: bge.un.s IL_0044\r\n\r\n    \/\/ return 'B';\r\n    IL_003f: ldc.i4.s 66\r\n    IL_0041: stloc.0\r\n    \/\/ (no C# code)\r\n    IL_0042: br.s IL_0047\r\n\r\n    \/\/ return 'A';\r\n    IL_0044: ldc.i4.s 65\r\n    IL_0046: stloc.0\r\n\r\n    \/\/ (no C# code)\r\n    IL_0047: ldloc.0\r\n    IL_0048: ret\r\n} \/\/ end of method MyGrade::Calc1\r\n<\/pre>\n<p>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&#8217;t seem to make a difference in speed.<\/p>\n<p>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.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>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&hellip; <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[104],"tags":[],"class_list":["post-5762","post","type-post","status-publish","format-standard","hentry","category-performance"],"_links":{"self":[{"href":"http:\/\/writeasync.net\/index.php?rest_route=\/wp\/v2\/posts\/5762","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/writeasync.net\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/writeasync.net\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/writeasync.net\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/writeasync.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=5762"}],"version-history":[{"count":2,"href":"http:\/\/writeasync.net\/index.php?rest_route=\/wp\/v2\/posts\/5762\/revisions"}],"predecessor-version":[{"id":5764,"href":"http:\/\/writeasync.net\/index.php?rest_route=\/wp\/v2\/posts\/5762\/revisions\/5764"}],"wp:attachment":[{"href":"http:\/\/writeasync.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=5762"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/writeasync.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=5762"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/writeasync.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=5762"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}