andyayers
We are a small tight-knit team, happy to both teach and learn new ways of making code run faster.
If you're curious about the kind of work we have been doing recently, check out the JIT section of https://devblogs.microsoft.com/dotnet/performance-improvemen... and or https://github.com/dotnet/runtime/blob/main/docs/design/core...
What else would we call it? It is what it is.
I believe there are some differences between what .NET does and what mainstream Java does. For instance, objects can be stack allocated even if they can't be turned into collections of scalars. This allows the JIT to stack allocate small known-sized arrays.
Linq contains a goodly number of hand-crafted special-case enumerators for common collections, or collections with certain interfaces, or span projections that are really nice optimizations but can complicate things for the JIT.
Some details here if you're curious: https://github.com/dotnet/runtime/blob/main/docs/design/core...
https://endjin.com/blog/2024/11/how-dotnet-9-boosted-ais-dot...
Some notes on why this is so here: https://github.com/dotnet/runtime/blob/main/docs/design/core...
With BenchmarkDotNet it may not be obvious which scenario you intend to measure and which one you end up measuring. BDN runs the benchmark method enough times to exceed some overall "goal" time for measuring (250 ms I think). This may require many calls or may just require one.
The OSR transition happens here, but between .net8 and .net9 some aspects of loop optimizations in OSR code regressed.
See eg https://github.com/dotnet/runtime/blob/main/docs/design/core...
(where this is presented as a puzzle)....
(DevDrive + Defender's "performance mode")
The number of trackable lifetimes was 64 in .NET Framework but has been steadily increased in modern .NET and is now 1024, so it's rarely a capacity issue; but there are cases where we can't effectively reason about lifetimes.
For us another big drawback to conservative scanning is that any object referred to by a conservative reference cannot be relocated, since the reference might be live and is not guaranteed to be a GC reference; these objects are (in our parlance) effectively pinned, and this causes additional overhead.
Abstract interpretation models a potentially infinite set of program behaviors onto a simpler (often finite) model that is (soundly) approximate and easier to reason about (via Galois connections); here the analogy is to Galois Theory connecting infinite fields with finite groups. I often think about this when working on Value Numbering for instance.
Also (perhaps a bit of stretch) it's interesting to think of extending a computational domain (say integers) with additional values (say an error value) as a kind of field extension, and as with field extensions, sometimes (perhaps unexpectedly) complications arise (eg loss of unique factorization :: LLVM's poison & undef, or NaNs).
.NET 7 will be officially released in a month or so.
Currently C#/F#/VB support in Compiler Explorer relies on prejitting which has various issues.
For now, you might find more info by searching for COMPlus_TieredPGO. The DOTNET_ prefix is a recent addition.
It fares pretty well in [techempower](https://www.peachpie.io/2018/06/performance-progress-report....).