Matt Warren
mattwarren.org · Personal blog · on HN since 2014
Matt Warren, a .NET developer, writes about CLR internals, runtime design, performance and the history of .NET.
What HN saysCommenters call it an incredible resource for CLR internals and value its write-ups on generics, CoreRT and .NET talks.
49HN people shared it
24linked it in comments
175stories submitted
5.5Kstory points
3 years agolast shared
2014people per year2026
Most-shared posts
- 1How generics were added to .NET7 people · 244 points · first shared 2018 · 124 comments on HN
- 2CoreRT – A .NET Runtime for AOT5 people · 179 points · first shared 2018 · 75 comments on HN
- 3Is C# a low-level language?4 people · 169 points · first shared 2019 · 71 comments on HN
- 4Taking a Look at CLR Thread Pool 'Thread Injection' Algorithm4 people · 134 points · first shared 2017 · 16 comments on HN
- 5Monitoring and Observability in the .NET Runtime4 people · 119 points · first shared 2018 · 5 comments on HN
- 6Exceptions should be Exceptional (2016)4 people · 82 points · first shared 2016 · 74 comments on HN
- 7Research papers in the .NET source3 people · 190 points · first shared 2016 · 29 comments on HN
- 8Open Source .NET – 2 years later3 people · 165 points · first shared 2016 · 46 comments on HN
- 9Visualising the .NET Garbage Collector3 people · 149 points · first shared 2016 · 24 comments on HN
- 1068 things the CLR does before executing a single line of your code3 people · 137 points · first shared 2017 · 12 comments on HN
- 11A Look at the Internals of 'Tiered JIT Compilation' in .NET Core3 people · 94 points · first shared 2017 · 52 comments on HN
- 12Open Source .NET – 4 years later3 people · 23 points · first shared 2018 · discussion on HN
- 13Fast wildcard searches with trigrams3 people · 23 points · first shared 2015 · 8 comments on HN
- 14Stop the .NET GC Running with TryStartNoGCRegion()3 people · 7 points · first shared 2016 · 4 comments on HN
- 15Optimising LINQ-to-Objects3 people · 7 points · first shared 2016 · discussion on HN
What HN people wrote when they linked it
hdhzy · 2017-05-21
Excellent post. The blog [0] is an incredible boon for anyone interested in CLR internals.
I wonder if the author considered writing a book, kind of modern Shared Source CLI Essentials (that I read years ago and hold in high regard)?
[0]: http://mattwarren.org/
atombender · 2018-12-19
So where should I look? It doesn't seem to be a particularly popular or well-documented topic. The best I've found is this [1].
[1] https://mattwarren.org/2018/06/07/CoreRT-.NET-Runtime-for-AOT/
matthewwarren · 2018-12-14
> I also love that people are doing write ups about the source of .NET Core and it's internals.
Me too!
BTW, if you want more on the same topic check out http://mattwarren.org/2018/07/12/Presentations-and-Talks-covering-.NET… and http://mattwarren.org/2018/01/22/Resources-for-Learning-about-.NET-Int…
Skinney · 2018-08-31
> Bytecode or ‘Intermediate Language’ (IL) changes. The main
> place that the implementation of generics in the CLR differs
> from the JVM is that they are ‘fully reified’ instead of
> using ‘type erasure’, this was possible because the CLR
> designers were willing to break backwards compatibility,
> whereas the JVM had been around longer so I assume that this
> was a much less appealing option. For more discussion on
>this issue see Erasure vs reification and Reified Generics
> for Java. Update: this HackerNews discussion is also worth a read.
http://mattwarren.org/2018/03/02/How-generics-were-added-to-.NET/
shakna · 2017-07-11
The CLR is quite terrible at small programs, as you're seeing.
That being said, the huge "Image" section doesn't have to be as big as you're seeing. If you look at the breakdown [0], then you can see some debugging, in the form of "TraceDLL". He also mentions that the four biggest memory eaters: System.Private.CoreLib.dll, coreclr.dll, clrjit.dll and CoreRun.exe are probably bigger than normal, because he's using a debug version of the CLR.
Outside those five, every other piece of memory is shared, which changes how you see its allocation.
I'd expect it'd change a measurable amount, but as before... The CLR is best when handling bigger programs, where JITing can actually make sense.
[0] http://mattwarren.org/images/2017/07/Image%20Memory%20Usage.png
csharptwdec19 · 2021-03-18
There's a few challenges with doing something like this.
- arrays (which most collections use under the covers) will need to allocate `elements*size_of_struct`. Unless the structs are the same size, you'd have to do some level of thunking/offsetting.
- Even if they are the same size, when you're iterating the list, how would the runtime know whether it's `A`, `B`, or `C`? Structs don't have header information that can be used to infer the type, it's only whatever fields you've defined (with padding potentially). Part of 'boxing' is essentially thunking the struct by wrapping a methodtable for the struct into the object [0].
If you need something like polymorphism with structs, your best bet is to make the struct itself quasi-polymorphic (i.e. with enums) and/or start abusing generics in some fashion I haven't yet figured out.
[0] - https://mattwarren.org/2017/08/02/A-look-at-the-internals-of-boxing-in…
bad_user · 2018-01-17
On .NET Core's focus, that's not actually true:
http://mattwarren.org/2017/12/15/How-does-.NET-JIT-a-method-and-Tiered…
Android optimizes for battery life, but it's also worth noting that Dalvik was a really rudimentary JIT, having no benefits from JIT compilation, only drawbacks, the ART with AOT being a good upgrade.
But tiered compilation is in a different league, being about speculating what's going to happen depending on what the process has witnessed thus far. The point of tiered compilation is to profile/guard stuff at runtime and recompile pieces of code based on changing conditions, which is how you can optimize virtual call sites or other dynamic pieces, which you can't do ahead of time because the missing part is the decompiler which can revert optimizations based on invalidated conditions.
It's really interesting actually, because you can profile a C++ app and use that to optimize your AOT compilation, but the compiler is still limited by the things it can prove ahead of time, or otherwise it would be memory unsafe.
samsquire · 2023-05-29
Thank you for your reply.
I prefer reading documentation or technical deep dives - such as this one
https://mattwarren.org/2017/02/07/The-68-things-the-CLR-does-before-ex…
Is code more dense than English? Why do I understand more overall from this blog post than I would if I studied the codebase of the CLR for hours and hours.
I think it's to do with mental models. If developers were on all the same page of understanding the underlying mental models, we could all understand eachother's code because we all understood the same underlying things.
NovaX · 2023-03-14
You might enjoy the alternative approach taken by .NET's threadpool which uses hill climbing to dynamically adjust the number of threads. See this articles [1] and its references to Microsoft's papers on their design. Semi-relatedly, I used hill climbing to adjust the eviction policy in Caffeine (Java cache) to favor recency/frequency. While it seemed simple and naive, it worked surprisingly well and robustly.
[1] https://mattwarren.org/2017/04/13/The-CLR-Thread-Pool-Thread-Injection…
sremani · 2022-03-12
C# is not faster than Rust - but you can get near Rust performance with all the safety and memory management goodness.
On benchmark C# is giving go run for its money! But again, it is easy to get carried away by benchmarks. Go-routines are a killer feature, .Net async/await come with gotchas.
The weird space of .Net/.net is because the first 15 or so years - it was hostage to Windows. There by tainting its mind-share and engineering culture.
Links (a bit old ones about C# low level chops)
https://mattwarren.org/2019/03/01/Is-CSharp-a-low-level-language/
https://www.youtube.com/watch?v=mLX1sYVf-Xg
pjmlp · 2021-11-09
The actual history goes a bit differently, they already had generics working, however they weren't stable and management didn't want to delay the initial release.
https://mattwarren.org/2018/03/02/How-generics-were-added-to-.NET/
lukevp · 2020-07-24
Why the hate on C#? Are you perhaps thinking of languages like Ruby and Python and grouping them into the same bucket as C#?
This[0] shows an example of C# vs C++ having a 10% performance gap on ray tracing. There is also no reason that extremely performance sensitive parts couldn’t be rewritten in Rust or C or C++ and called from C# over a FFI.
[0]: https://mattwarren.org/2019/03/01/Is-CSharp-a-low-level-language/
throwlaplace · 2019-12-26
>Does F# offers native builds ? I thought it was CLR only.
i would guess that it will eventually support aot:
https://mattwarren.org/2018/06/07/CoreRT-.NET-Runtime-for-AOT/
MarkSweep · 2019-10-14
It looks like the number of threads in the threadpool is fixed. If have 1 one thread in the thread pool, is it possible to dead lock it by queuing a Task A the blocks waiting for Task B to do something, where A starts executing before B?
Do you think it would make sense to dynamically adjust the number of threads in the pool? An example of a thread pool that dynamically adjusts the number of threads is .NET. The algorithm is described by Matt Warren[1] and a paper[2]. In addition to the C++ version Matt links to, there is a C#[3] implementation.
[1]: https://mattwarren.org/2017/04/13/The-CLR-Thread-Pool-Thread-Injection…
[2]: http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.487.7627&…
[3]: https://github.com/dotnet/corert/blob/master/src/System.Private.CoreLi…
acqq · 2019-03-25
If I understand correctly, that uses this:
https://github.com/dotnet/corert
Written about here:
https://mattwarren.org/2018/06/07/CoreRT-.NET-Runtime-for-AOT/
"One of the first things people asked about CoreRT is “what is the size of a ‘Hello World’ app” and the answer is ~3.93 MB (if you compile in Release mode), but there is work being done to reduce this."
..
"So Test.CoreLib really is a minimal runtime!! But the difference in size is dramatic, it shrinks down to 0.49 MB compared to 3.93 MB for the fully-featured runtime!"
tigershark · 2019-03-17
C# compiler can generate a huge amount of code from simple and small programs:
https://mattwarren.org/2017/11/08/A-DoS-Attack-against-the-C-Compiler/
But you can very easily get the AST.
Obviously it may happen that you have to wait until the hell is frozen to get a result, but generally it doesn’t happen in code that you see day to day..
benaadams · 2019-01-27
Any obvious reasons currently; or just historic ones?
i.e. its been fully open source for 4 years https://mattwarren.org/2018/12/04/Open-Source-.Net-4-years-later and is under the .NET Foundation rather than Microsoft https://dotnetfoundation.org/
cmiles74 · 2018-07-19
I agree that generics should probably be a strong consideration right from the beginning. I disagree that this would mean that it would necessarily be done correctly or that it's impossible to add it later. That was the case with .Net and C#, it wasn't until the 2.0 release that generics were added. [0]
[0]: http://mattwarren.org/2018/03/02/How-generics-were-added-to-.NET/
mandeepj · 2017-07-04
Some more related links to .net micro fw -
http://mattwarren.org/2017/03/30/The-.NET-IL-Interpreter/
https://github.com/NETMF/llilum
http://www.netmf.com
tjalfi · 2017-05-11
TypeMock Isolator[0] and JustMock[1] can mock nonvirtual methods and static methods. Fody Ionad[2] is a free tool that can replace static methods. The reason that most mocking framework don't support this is that the implementation is complex[3].
[0] https://www.typemock.com/isolator
[1] http://www.telerik.com/products/mocking.aspx
[2] https://github.com/Fody/Ionad
[3] http://mattwarren.org/2014/08/14/how-to-mock-sealed-classes-and-static…
flukus · 2017-05-01
.Net now has a non-hierarchical version of this:
http://mattwarren.org/2016/08/16/Preventing-dotNET-Garbage-Collections…
SloopJon · 2016-05-09
This post suggests that Stack Overflow has about 30,000 distinct tags, which seems like a good fit for a bitmap index:
https://mattwarren.org/2015/08/19/the-stack-overflow-tag-engine-part-2…
Not that that's the whole solution, but certainly something I'd look into before digging into CUDA.
skybrian · 2021-11-09
Java did this in a very complicated but backwards-compatible way. (Including bytecode level compatibility.) Here is a detailed FAQ [1] devoted to the intricacies of Java generics, covering erasure, bounded and unbounded wildcards, special cases for raw types and arrays, workarounds for primitive types, interaction with inheritance, etc.
It was a big achievement, but also something to learn from. It’s reasonable that other language designers would want to do better.
The C# designers tried to improve on what Java did, reconsidering many of their design decisions [2]. Apparently it was a five year effort, including inventing novel runtime mechanisms. They had different goals, particularly supporting multiple languages and cross-language compatibility.
Go’s generics support is another years-long effort. They reconsidered everything again in a new context, because the Go language doesn’t have the same features or design constraints. That’s also a big achievement.
This only looks like the “exact same thing” if you ignore all the history. Language design is all about the details. Every language feature interacts with every other language feature.
The “peanut gallery” is a lot of random people, some of whom don’t know anything about designing or maintaining a production language. If you really want to know about this stuff, I suggest reading the papers by people who actually did it.
[1] http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html
[2]…
algorithmsRcool · 2017-01-10
For those interested in more details about .NET garbage collector here is some reading.
Garbage Collection Overview
https://msdn.microsoft.com/en-us/library/0xy59wtx(v=vs.110).aspx
Fundamentals of garbage collection
https://msdn.microsoft.com/en-us/library/ee787088(v=vs.110).aspx
Visualising the .NET Garbage Collector
http://mattwarren.org/2016/06/20/Visualising-the-dotNET-Garbage-Collec…
Clr Book of the runtime : Garbage Collection
https://github.com/dotnet/coreclr/blob/master/Documentation/botr/garba…
Maoni Stephen's Blog
https://blogs.msdn.microsoft.com/maoni
https://blogs.msdn.microsoft.com/maoni/2004/06/15/using-gc-efficiently…
https://blogs.msdn.microsoft.com/maoni/2004/09/25/using-gc-efficiently…
https://blogs.msdn.microsoft.com/maoni/2004/12/19/using-gc-efficiently…
https://blogs.msdn.microsoft.com/maoni/2005/05/06/using-gc-efficiently…
There is also a refactoring effort to standardize the GC<->EE interface
https://github.com/dotnet/coreclr/projects/3
I would link to the source code of the GC itself, but it's a single 35KSLOC file.