Lowering in C#: What's really going on in your code? - David Wengier
ฝัง
- เผยแพร่เมื่อ 29 ธ.ค. 2024
- If you're attending NDC you probably think you know what a foreach loop does - it iterates over a collection, right?
Well... yes.
BUT do you know how? Do you know what the C# compiler does when you write a foreach loop? What about a lambda expression? Or the re-entrant magic that is a yield return statement?
In this session we'll dive into Roslyn, the C# compiler, and learn about lowering and how it helps the compiler do its job, and what it does to your code. In the process you'll gain the skills to identify some of the common performance pitfalls of .NET, as well as just get a deeper understanding of what the code you write really does.
Check out more of our talks in the following links!
NDC Conferences
ndcoslo.com
ndcconferences...
I love this kind of videos, great explanation and examples!
Can't say I got everything mentioned in this topic, but this is different from what you usually find in web nowadays. Thanks for sharing!
There's a lot of stuff that I got to know about the side effects of LINQ thanks to this video. Looking forward to similar stuff.
I wonder who long it would've taken to explain how async-await is when lowered
They're surprisingly similar, I was actually thinking that the second half could have been that instead of going in to "yield", but then the similarities would be lost.
Another NDC talk said that the String class does not have an add operator, this one claims the Concat function is faster than the add operator. So one of them made a mistake :) The other talk was about Expressions.
Strictly speaking there isn't an add operator implemented in the base class library on the String, class because there doesn't need to be because of the compiler optimization that changes the calls to Concat, but of course the + operator is still valid code. This is one of the times I had to simplify things to better communicate the point, apologies if you were confused by it.
Regarding the string format it seems you still can't use it with a const string and let's say a const int inside { } which is a shame unless it's been fixed now?
I dont see the point of lowering, is intresting to see how to improve performance, but I prefer legibility over performance, the code is written for us, for the devs, not for the computer, then de compiler translates it to the computer, so I think I`m missing the point, or is only for educational propuses? Can David o someone clarify me?
It's mainly educational and interest for me, but it is relevant when solving performance problems. Knowing what to avoid in hot paths can be very important, but obviously software development is a very varied field, and there is absolutely nothing wrong with prioritizing readability if its the right choice for your scenarios.
There was actually some in here I didn't know yet. I can be good to know about lowering, esp when trying to optimize your code. But talking too much about "peformance" in combination with lowering in such a talk I think may mislead developers to premature micro-optimization, instead of writing readable, maintainable code. Also, I was kinda disappointed this was mostly only about foreach loops (and delegates / iterators), when there are so many other interesting lowering cases, such as async/await, using, and many pattern matching expressions (and probably more which I don't even know about yet).
So in the end of the day, foreach on List gonna be for loop or IEnumerable?
No, it uses the indexing as shown in the last minute
Actually
1) Enumerator is non-nullable, so null check not needed. i.ibb.co/25Hyp5P/image.png
2) Even with other types, type comparsion also checks for null automatically. Because null has null type. Separate null check also not needed. i.ibb.co/C8HGJBv/image.png
Thanks for your feedback. Are you referring to the null check inside the finally block?
You're right of course, but I had to take a little "poetic license" to represent the code in C#. In reality the compiler does the lowering at the "Bound Node" level, not in true C# code, and so those checks are not even present - the compiler knows if the return from GetEnumerator() can be disposed, and if not it simply elides the call altogether. The null check and cast were just my way of representing that in C#. I will try to remember to explain this better if I deliver the talk again :)
@@DavidWengier yeah, I understand that, your topic is very interesting, I'm just clarifying a bit :)
By the way, also some internals may differ for different types. For example, "foreach" is not really used for arrays. It just converts to plain "for" loop during compilation. i.ibb.co/xgcxLjV/image.png
So for arrays you may not afraid to lose performance because of foreach. (But in case with lists you are losing performance drastically because of all that enumerator manipulations and try-catch blocks.)
@@mk72v2oq Nice explanation with images 🙂
Good info, however after 35 minutes I hadn’t learned anything I could actually apply...
So true
aimless meandering…