The Switch Keyword in C# is a LIE!

  Рет қаралды 43,378

Nick Chapsas

Nick Chapsas

Жыл бұрын

Check out my courses: dometrain.com
Become a Patreon and get source code access: / nickchapsas
Hello everybody I'm Nick and in this video I will show you what's really behind C#'s Switch keyword. There are many assumptions about how a switch works behind the scenes but in this video we will debunk all that.
Workshops: bit.ly/nickworkshops
Don't forget to comment, like and subscribe :)
Social Media:
Follow me on GitHub: bit.ly/ChapsasGitHub
Follow me on Twitter: bit.ly/ChapsasTwitter
Connect on LinkedIn: bit.ly/ChapsasLinkedIn
Keep coding merch: keepcoding.shop
#csharp #dotnet

Пікірлер: 111
@edbutler3
@edbutler3 Жыл бұрын
Interesting stuff, especially how the compiler builds a binary search in some scenarios. One takeaway for me (which maybe should have been obvious anyway) is that the order I write my switch cases in the source code has no impact on performance once compiled. Putting the most common case at the top, for example, has no effect.
@billy65bob
@billy65bob Жыл бұрын
depends, C# technically has two distinct switch statements: 1. Testing for value; this is the traditional one, and what nets you either a lookup table or the binary search. 2. Testing for types (aka C# 7.0's pattern matching), these are evaluated in the exact order you specify them; and the order does matter here when interfaces enter into the equation. e.g. just try putting an `IEnumerable` case before a case for `string` :)
@peterjosefsson9857
@peterjosefsson9857 Жыл бұрын
That pretty much matches what I was taught about the switch statement in C back in the 80's. I think the book said something like "the compiler is allowed to make it an if-else ladder, but most compilers will try to optimize it into something smarter". Modern compilers are of course smarter, but it would surprise me if most of the optimizations we saw here haven't been around in C compilers (and, most likely, C# since its inception) for ages. I've never looked into it, though, but I saw no "new" algorithms there.
@tmhchacham
@tmhchacham Жыл бұрын
Optimization is nice, and sometimes required. But readability is king. A switch statement makes it overwhelmingly obvious what is being done, so i'd take that into the equation.
@Krokoklemmee
@Krokoklemmee Жыл бұрын
yeah, but you barely ever need to read the optimized form anyway, so use whatever is appropriate
@OllyWood688
@OllyWood688 Жыл бұрын
3:52 can't imagine you don't know this, but holding Alt+Shift lets you place multiple cursors. In this case, you could have placed the cursor behind the 1 on line 5 as you did, then hold Alt+Shift, then hit the down arrow nine times and press 0 once.
@danda-dandic
@danda-dandic Жыл бұрын
You can also hold down middle mouse button and drag
@mov4736
@mov4736 Жыл бұрын
Thanks! I didn't know this :D
@tmhchacham
@tmhchacham Жыл бұрын
Wow! I didn't know that. Thank you!
@kacperwyczawski618
@kacperwyczawski618 Жыл бұрын
Or select =>, press alt + shift + ; and now you have your cursors in all lines
@bdcp
@bdcp Жыл бұрын
the real tip is always in the comments
@czajla
@czajla Жыл бұрын
I remember in C times I checked what compirer did to optimize. Code was to copy 7 bytes in a loop. Compiler generated (in a 8080 family assembly) sequence of movdw, movw, movb. (move double word, move word, move byte)
@billy65bob
@billy65bob Жыл бұрын
When I did C++, I usually went out of my way to use switch for expansive and usually mutually exclusive cases. I was under the impression that the compiler would usually see my usage and say "I can optimise this into a lookup table!" and give me really good perf that way. Nice to see that C# keeps that alive. I did know of the binary search; I had decompiled a switch statement out of curiosity that switched on a string, and lo what I saw was it storing the hash into a temporary and doing a binary search based on the hash codes of the cases. I remember thinking that was clever. With that said, it feels like the C# compiler really only does 3 things: 1) inlines constants and glues strings together where possible 2) eliminate unreachable code 3) do this to the switch statements. Everything else is for the JIT to do.
@judas1337
@judas1337 Жыл бұрын
Thank you for spreading this! I found it earlier this year when a complexity metric complained about how complex simple switch cases where. Last Saturday I found the result to be the same for chaining ternary-operators…
@zvenlin
@zvenlin Жыл бұрын
i did not expect the string to be optimized that way, cool
@yondaime500
@yondaime500 Жыл бұрын
I was actually benchmarking that the other day in Rust. I had a list of 60 TypeScript keywords, and tried 4 different methods of finding the index of a word in the list. For each method, I tried searching for each of the 60 words and calculated the average time: - linear search: 19ns - binary search: 16ns - hashmap: 14ns - match statement: 8ns So yeah, the compiler can do some pretty clever stuff with a switch statement I guess. But I got the idea to test this from a channel called strager, where he hand-crafted a hash function in C++ specifically for this dataset and did a bunch of tricks to get it down to less than 3ns. So you can beat the compiler if you're really motivated.
@swordblaster2596
@swordblaster2596 Жыл бұрын
now this is genuinely fascinating. The compile time optimisations are fascinating, allowing me to write human readable code and still getting optimised output is always the goal!
@jonbellamy
@jonbellamy Жыл бұрын
Did no one else spot the "80085" seed? 🙂 Great stuff as always Nick!
@johnpekkala6941
@johnpekkala6941 Жыл бұрын
Just wondered where he did get that number from. Guessed it was some kind of seed but why that exact number?
@jonbellamy
@jonbellamy Жыл бұрын
@@johnpekkala6941 🤣 Change those numbers to the closest capital letter! 😂 Or grab an old calculator, type 58008 in and turn it upside down 🤣
@dnwheeler
@dnwheeler Жыл бұрын
This is very similar to what C and C++ compilers have been doing for years (decades). In general, I use the construct that is the most natural and understandable for the circumstance without trying to second-guess the compiler.
@shakeuk
@shakeuk Жыл бұрын
Ha, I've been waiting for this video! since we talked about this what happens with switch statements before when I left a comment on a previous video 😊
@TampaCEO
@TampaCEO Жыл бұрын
To be honest with you, I've NEVER used a switch statement on a STRING. In fact, the ONLY time I've used a SWITCH statement is for an ENUM type. Enums in essence are an enumerable integer - which in turn must be creating a "jump table" (for standard unassigned enum values of course).
@zer0K3lvin
@zer0K3lvin Жыл бұрын
Wouldn't the most common use case for switch be an enum? And from my understanding this would then be the jump table implementation (as the underlying data type is usually an int) - so the best case?
@phizc
@phizc Жыл бұрын
It used to be the most common, probably.. Now that we can so much more, like the following, I think the it and the various other use cases has made it a large minority.. switch( obj ) { case int x when x is > 20: return x; case int x: return x*5; case string x: ... case MyType x: ... }
@chris-pee
@chris-pee Жыл бұрын
Even with enum it won't always be a jump table. You need sequential values for that. Remember that you can assign any integer value to enum members. And sometimes you have to, especially when your enum has multiple values ( [Flags] )
@coolimdad
@coolimdad Жыл бұрын
I approve of the seed you used at 7:58
@Xankill3r
@Xankill3r Жыл бұрын
I think you should do a follow-up video comparing this with the upcoming Frozen collections dictionary.
@nickchapsas
@nickchapsas Жыл бұрын
Same speed, especially after the recent changes
@elraito
@elraito Жыл бұрын
One of these days ill get to write code where 2 nanosecond win matters more than database access times. Imo honestly at these scales you need a super specific problem to bother with optimizing code for performance over just shipping the damned thing
@paulkoopmans4620
@paulkoopmans4620 Жыл бұрын
Just because your database call takes a long time... doesn't mean you should be adding more then necessary. And this has come up soooo many times... that yes while this looks insignificant on the scale of a single test.... if you have it in a hot path... you might still care about it. Like... to your friend... oh you are carrying the 80lbs box? then... I'll just add this other 20lbs box. Come on bud... you can do it.
@Yous0147
@Yous0147 Жыл бұрын
I actually thought switch cases would always optimise to a table, so this is eye opening and very interesting to me. It's really cool to understand what actually works for a switch and when it might be better to use a dictionary.
@urbanelemental3308
@urbanelemental3308 Жыл бұрын
Perf benefits with switches only happen with integers or the like (enums, etc). Really great points. I had no idea that it optimized strings if it can.
@MechMK1
@MechMK1 Жыл бұрын
The vast majority of switch statements I've seen "in the wild" are on enums, so in these cases it's also pretty fast. Also, beware of micro-optimizations! Remember that code is not just there to be executed, but also understood. Sacrificing a tiny bit of performance for higher readability may definitely be a worthwhile tradeoff. Or, to put it another way: You would not want to turn your readable code into an unreadable mess to save 10kb of memory efficiency.
@urbanelemental3308
@urbanelemental3308 Жыл бұрын
@@MechMK1 Totally. I admit I'm a bit of micro-optimizer, but I won't sacrifice readability.
@diadetediotedio6918
@diadetediotedio6918 Жыл бұрын
The only sad thing about switch expressions is that we can't just have bodies and they need to return values, the old switch case break syntax is kinda ugly and less ergonomic
@theMagos
@theMagos Жыл бұрын
If I understand you correctly, you CAN have blocks in switch-cases: case 1: { //Stuff break; }
@agsystems8220
@agsystems8220 Жыл бұрын
Its moments like this that really make me facepalm at the IL design. The compiler is being helpful, but at the cost of stripping away information that could be used by the JIT. That binary tree might be optimal given the information the compiler has, but it doesn't take into account how the code will actually be used. You might find that 90% of the time a certain branch is followed in a particular situation. If the JIT were to know that, and if you tell it that the tree is only a suggested implementation of a switch it could rearrange it to give an early out. Clever JITs may still spot that, and do it anyway, but the fact we have programmers explicitly stating their intent, the compiler stripping that out, and then the JIT re-inferring it in the runtime rush cannot be optimal. It is like the IL was designed around real world hardware, despite the fact that nothing exists, or is expected to exist, that runs it natively. They completely strip out higher level concepts that are extremely primitive and powerful. High level concepts are not in most instruction sets because they do not map well to real hardware, but an LLVM is not real hardware. Map and Fold could easily be part of a JITed instruction set, as could multi directional branching (Switch) with advanced software branch prediction and the ability to provide additional (equivalent) implementations of a block that the 'hardware' is free to choose from. Map is a particularly annoying omission, because that actually does translate to real hardware. Wouldn't it be great if the machine would respond to a simple calculation on an array a million objects long by sending it to the integrated graphics? LLVMs are capable of handling arbitrarily complex instruction sets, so why on earth don't we use that to express what we actually want. In this particular case we do not want a binary tree search, we want a switch expression. The binary tree is just a detailed implementation that the compiler thinks will work well anywhere, but there is no distinction between the two, and no clarity to the JIT on what we do and what we don't care about. Rant over... Great info as always, mostly commenting for the algo.
@jongeduard
@jongeduard Жыл бұрын
Great video. I can say that I definitely knew that a switch does not always directly get translated to the switch in IL, actually in the past it surprised me that IL even has such a thing when I discovered it. But I know IL's switch is limited. Though what is more surprising to me is how much the compiler does in total! It's so smart! Nevertheless, I don't call the switch keyword a lie. In the end C# has always had a very high level syntactic sugar anyway, so this is just another thing of that. And many other programming languages do all those kind of things as well. And, finally, the next discovery you will make is that IL is sugar as well, the real ASM generated from that is very different again - JIT is very smart too. It's all relative.
@DraygoKorvan
@DraygoKorvan Жыл бұрын
There is another optimization I've seen when comparing a lot of strings of equal length the code will be optimized to get the hash value of the string then do a binary search on the hash values.
@izobrr
@izobrr Жыл бұрын
Also interesting things happen when your values are distributed in several contiguous ranges. Like 1 2 3 4 7 8 9. The compiler then still uses a jump table, but before does something like if(x>4){if(x
@gokn6462
@gokn6462 Жыл бұрын
what do you guys think about blazor? is it good for production?
@mikicerise6250
@mikicerise6250 Жыл бұрын
I had no idea! Thanks for this video. :)
@F1nalspace
@F1nalspace Жыл бұрын
Very interesting, didn´t knew that. I was expecting that the compiler wont optimize the switch case at all and simply tests all cases, except for "Fall-through" cases. So the take from this is to try to help the compiler to create a jump table if possible always. Btw. it the IL Viewer free or is it part of the very expensive ReSharper suite?
@paulkoopmans4620
@paulkoopmans4620 Жыл бұрын
I believe it is part of ReSharper too.. but in Nick's case... and mine... in Rider, I think it is included. And please stop saying that Rider and ReSharper are expensive. $149 for Rider, or splurge! and $169 for SIX very awesome tools is NOT expensive! - Wood workers, tile setters, general contractors spend more that that annually on tools and wear items. - Graphic designers ( just PS is $336! ), Most used apps bundled together sets them to $864.
@vynxc.
@vynxc. Жыл бұрын
Shit i get rider for freeeee
@paulkoopmans4620
@paulkoopmans4620 Жыл бұрын
@@vynxc. nice! Well there is that too... but not everyone can get it for fee. But at least everyone can buy a personal copy.
@haukurorsson833
@haukurorsson833 Жыл бұрын
Fascinating stuff. I've never thought about this aspect of the switch expression.
@shmupful
@shmupful Жыл бұрын
Thank you, this is very interesting. Can I investigate the low level / IL codes using Visual Studio also?
@nickchapsas
@nickchapsas Жыл бұрын
I’m pretty sure that VS has ILSpy that allows you do check the IL, but I don’t think that you can see lowered code.
@izobrr
@izobrr Жыл бұрын
Actually in early versions of C# switch by strings was always converted to switch by ints using generated dictionary cashed in a static field. It was true at least up to .Net 3.5.
@anonimxwz
@anonimxwz Жыл бұрын
Can you compare a switch with a Dictionary???
@457Deniz457
@457Deniz457 Жыл бұрын
Thats so perfectly timed haha I just took a research about what really happens by using switch 😁 Thanks 😊
@user-tj2by9zp6c
@user-tj2by9zp6c Жыл бұрын
Thanks for very interesting and useful information, Nick! Brilliant as always)
@liquidcode1704
@liquidcode1704 Жыл бұрын
low level c# of non-sequential switch statement just gave uncle bob a heart attack
@diadetediotedio6918
@diadetediotedio6918 Жыл бұрын
It doesn't matter much what the compiled code appears to be
@Esgarpen
@Esgarpen Жыл бұрын
I've always been told that when you pass 7 if-elseif-else statements you should move over to switch clause instead, never questioned it... never looked into it... good to know compiler actually does compiler stuff and it doesn't really matter - it's more a question of readability I suppose...
@ABC_Guest
@ABC_Guest Жыл бұрын
I always just assumed that a switch statement was just syntactic sugar for an extended if-else statement. Didn't realize the compiler would treat it so differently. Interesting.
@andreilugovoy4592
@andreilugovoy4592 Жыл бұрын
I didn't know! Mind blowing
@lordicemaniac
@lordicemaniac Жыл бұрын
i knew there was optimization behind the scenes, but i also thought that when you get to high numbers in switch, it will switch to some kind of data structure like dictionary... does it not?
@johanngerell
@johanngerell Жыл бұрын
*only* jumping is always faster than calculating a hash (which is what a dictionary will do) before jumping, so when the compiler *can* implement the switch as jumps, it will
Жыл бұрын
I checked this out a long long time ago (like 10 years maybe), and if I remember correctly, the C# compiler actually used a Dictionary on string switches when the amount of strings got over a certain threshold.
@johanngerell
@johanngerell Жыл бұрын
@ @lordicemaniac referred to number-switching, not string switching
@krccmsitp2884
@krccmsitp2884 Жыл бұрын
Ok, that I didn't expect. Thanks for showing!
@philosophersam
@philosophersam Жыл бұрын
Great stuff, as always.
@phizc
@phizc Жыл бұрын
Would be cool to add the new frozen dictionary to that benchmark. It was kinda surprising that the dictionary was only 10% slower than switch, even with the wildly different strings.
@nickchapsas
@nickchapsas Жыл бұрын
Frozen dictionary and normal dictionary are extremely close on reads. We are talking half a nanosecond difference
@phizc
@phizc Жыл бұрын
@@nickchapsas yeah.. I went back and rewatched the video you made on Frozen Collections, and while you didn't benchmark a normal Dictionary along ImmutableDictionary and FrozenDictionary, I was able to find your answer to a question about it. 2.2ns vs 2.9ns or something like that.
@egoegoone
@egoegoone Жыл бұрын
Hmm. Might need to switch up when I use it.
@DummyFace123
@DummyFace123 Жыл бұрын
Well also the switch is also code blocks and fallthrough, and dict is just a lookup
@cn-ml
@cn-ml Жыл бұрын
Maybe a FrozenDictionary would be as fast as the switch, maybe even faster
@scaly86
@scaly86 Жыл бұрын
80085. Nice Great video dude, really cool to take a deeper look at things devs usually tend to take for granted.
@IvanRandomDude
@IvanRandomDude Жыл бұрын
If-else
@BGFutureBG
@BGFutureBG Жыл бұрын
"A disjointed sequence doesn't allow the compiler to build a jumptable" ... but ... why? 😅
@ConnorZ
@ConnorZ Жыл бұрын
“that is not the case” no pun intended
@alexxeken
@alexxeken Жыл бұрын
I wonder how many times Nick have said "however" in all of his videos. Haha
@suleymanov1990
@suleymanov1990 Жыл бұрын
I like in switch when clause, which is missing in dictionary
@anm3037
@anm3037 Жыл бұрын
I laugh at those who rely on compiler only for optimization. It’s very easy to claim that the compiler will optimise without actually checking it. Thanks nick
@cwevers
@cwevers Жыл бұрын
Interesting. Did not know
@austinwasinger
@austinwasinger Жыл бұрын
7:58 nice.
@saecula2391
@saecula2391 Жыл бұрын
lol .. 05:49 .. at 7 the compiler optimizes
@donelbaron
@donelbaron Жыл бұрын
i am wondering if just checking which optimalisation rule to apply takes more time then just iterate by all values for such little values like 10
@phizc
@phizc Жыл бұрын
That's done at compile time.
@klocugh12
@klocugh12 Жыл бұрын
A switch with large amount of cases probably isn't good from readability standpoint alone and calls for some refactoring anyways.
@sunnypatel1045
@sunnypatel1045 Жыл бұрын
This is why I use a strategy pattern.
@ChuckAsum
@ChuckAsum Жыл бұрын
Here is a good argument for pre-optimizing vs relying on what the compiler already does for you. 😊
@digitalman2112
@digitalman2112 Жыл бұрын
My whole life's been a lie
@shingok
@shingok Жыл бұрын
Seems pretty magical to me
@TheLilRussia
@TheLilRussia Жыл бұрын
80084 out of 80085 Developers recommend your videos.
@user-hb7py7xy7b
@user-hb7py7xy7b Жыл бұрын
Tbh, I always assume that switch is just sugar for multiple if else.
@JoeIrizarry88
@JoeIrizarry88 Жыл бұрын
🤯🤯
@TanelTeede
@TanelTeede Жыл бұрын
1-up for 80085 at 8:00
@stephaneforget4693
@stephaneforget4693 Жыл бұрын
Switch are great for enum
@_grigoryta
@_grigoryta Жыл бұрын
The thing about switch is that it's usually uglier than a dictionary. When it grows to around 5 cases and more. Readability over a small memory allocation all the time.
@domportera
@domportera Жыл бұрын
80085 😏
@samphire
@samphire Жыл бұрын
hahaha 80085 :)
@jellybean2682
@jellybean2682 Жыл бұрын
If AI models such as GPT-4 are likely to replace us then why continue to making videos such as this? Is this pointless since we’re all going to be eventually replaced? Aren’t you just creating more training data for AI models such as any model Google creates to become even smarter at programming … These horrible questions can’t help but escape my mind after seeing your video and many others on GPT 4
@evancombs5159
@evancombs5159 Жыл бұрын
Why is that horrible? Isn't that the goal of technology, to make our lives easier?
@GuilhermeMG
@GuilhermeMG Жыл бұрын
80085
I Lied! The Fastest C# Loop Is Even Weirder
11:50
Nick Chapsas
Рет қаралды 45 М.
How to write "smarter" enums in C#
12:56
Nick Chapsas
Рет қаралды 133 М.
3 .NET "Best Practices" I Changed My Mind About
10:16
Nick Chapsas
Рет қаралды 99 М.
why are switch statements so HECKIN fast?
11:03
Low Level Learning
Рет қаралды 376 М.
Every New Feature Added in C# 12
14:19
Nick Chapsas
Рет қаралды 147 М.
Inject C# In Any .NET App With This Secret Entry Point
15:06
Nick Chapsas
Рет қаралды 51 М.
My 2 Year Journey of Learning C, in 9 minutes
8:42
VoxelRifts
Рет қаралды 547 М.
Optimising Code - Computerphile
19:43
Computerphile
Рет қаралды 140 М.
Settling the Biggest Await Async Debate in .NET
14:47
Nick Chapsas
Рет қаралды 139 М.
5 C# Naming Conventions I Wish Everyone Followed
12:46
Amichai Mantinband
Рет қаралды 15 М.
Build Clean Messaging in .NET with MassTransit
21:44
Nick Chapsas
Рет қаралды 91 М.