.NET 6 vs .NET 5 speedup
alexyakunin.medium.com
alexyakunin.medium.com
set DOTNET_ReadyToRun=0
set DOTNET_TieredPGO=1
set DOTNET_TC_QuickJitForLoops=1
Here are my own benchmarks from a CPU intensive application without any IO and already optimized for allocations. Application runs a task graph either serially or in parallel. .NET 5
--------------------------
| Method | Mean |
|------------ |---------:|
| RunParallel | 473.4 us |
| Run | 513.5 us |
.NET 6
--------------------------
| Method | Mean |
|------------ |---------:|
| RunParallel | 452.5 us |
| Run | 499.8 us |
.NET 6 PGO
--------------------------
| Method | Mean |
|------------ |---------:|
| RunParallel | 381.8 us |
| Run | 412.2 us |
.NET 5 - .NET 6 -> ~5%
.NET 5 - .NET 6 PGO -> ~20%
Here is what I learned from micro-optimizing a .NET application:- Use BenchmarkDotNet[0] for general measurements and Visual Studio profiler tools for detailed inspection. They help a lot.
- Memory allocations matter. Using capturing lambdas, LINQ, even foreach on interfaces introduce allocations and slows down the application. You can use ClrHeapAllocationAnalyzer[1] to find these hidden allocations.
- Using abstractions with interfaces and casting back to concrete types cause some overhead, though PGO will probably eliminate most of these.
- Use LINQ cautiously as its variants are mostly slower than explicit coding. E.g. .Any() vs .Count == 0
- Checking Logger.IsEnabled() before calling Logger.Debug() etc. helps a lot. You can even automate this with Fody [2], but it breaks Edit&Continue and possibly .NET Hot Reload too, so it may hinder your productivity.
[0] https://github.com/dotnet/BenchmarkDotNet
[1] https://github.com/microsoft/RoslynClrHeapAllocationAnalyzer
When using LINQ also be aware that .First(predicate) is significantly slower than .Where(predicate).First() when called on List<T> and T[]. This is true for essentially all methods like Last, Single, Count etc. Don't trust Visual Studio when it's telling you to "optimize" this.
But if you want the last bit of performance, you shouldn't use LINQ anyways.
The difference, as @sbelskie already mentioned, is that Where has an optimization for List, while First only uses the naive enumerator.
I'm interested, what's the advantage for you to using ILogger instead?
Is this really true for the example? To me it seems that the implementation for .Any actually uses .Count when available, see https://github.com/dotnet/runtime/blob/main/src/libraries/Sy...
I turned it on mostly to show what you can expect from a service that runs for a while (more than a few minutes?) in a typical server-side scenario after migration to .NET 6 - IMO it's totally reasonable to turn PGO on for nearly any service of this kind.
On a positive side, I can assure you I know how to run such benchmarks properly - i.e. things like warmup, explicit GC before / after are just some of the aspects taken into account. If you're curious about some other benchmarks I ran in past, check out https://itnext.io/geting-4x-speedup-with-net-core-3-0-simd-i... and https://github.com/alexyakunin/GCBurn
Eg., highly standardised docker builds, on highly standardised hardware, running popular tasks using popular libraries for each "programming tech" (eg., website, stat modelling, event system, ...).
https://github.com/TechEmpower/FrameworkBenchmarks/wiki/Proj...
They often strip out framework functionality and hyper-optimise for the specific benchmark, including things like pre-allocating the exact amount of memory needed to serve the request, not doing route matching et all, etc.
They are basically an exercise in "how clever can we be to win the benchmark" rather than a realistic portrait of real world performance.
But still, these benchmarks have their uses but there are a lot of caveats you need to consider when looking at the results.
Do you really believe that Merch sales and sponsorship by Squarespace, VPN providers, sys-admin software, etc influence their coverage of CPUs?
Really, writing fast code is mostly down to the programmer. For example C is widely recognized as the fastest non-assembly language simply because it leaves a lot to the programmer, C won't magically make your terrible code fast, unless you are using time-to-segfault as a metric. Assembly is the fastest if you know what you are doing, very few know what they are doing.
So, what kind of code are you going to use for your benchmark? Highly optimized code written by experts spending way too much time, the "most idiomatic" code, code written by an average skilled programmer picked at random, code extracted from a big open source project? This can drastically change the ranking, so which one is the most relevant? If you go with the "most idiomatic" for instance, you miss out on the idea that parts can be optimized if needed, and that in real life, programmers aren't perfect and can write suboptimal code by mistake.
There is also a cultural aspect to languages that may not be caught in benchmarks. For example, C programmers tend to have a culture of performance, they tend to know about their hardware, will try to save memory, make data structure efficient, etc... Python programmers, not so much, instead they tend to value readability and development time.
You can't test languages like you test CPUs for instance. With CPUs, you just run the same code on them and time them. You can't do that for obvious reasons: your C compiler won't accept your Python code, it is necessarily an apples to oranges comparison.
Just a nitpick but for any reasonably sized code, no. While some people can indeed do impressive optimizations on small segments of assembly, they are humans and they will fail to do trivial optimizations that are reliably done by compilers.
-unneeded allocations
-boxing/unboxing
-garbage collection
-interpreting
But even if the GC is really concurrent: If there's a way of not doing that work it's still better, IMHO.
One interesting profile I've seen at work recently spent about 30 % creating objects, and another 35 % in garbage collection (of pretty much the same objects that have been created all the time). So if there was a way of not allocating that much, or not doing it on the heap, the algorithm could be about twice as fast.
Also, Java can often accumulate big heaps because it only runs the GC when it absolutely must — as you mentioned, it would be unnecessarily work otherwise. It might be interesting to mention that OpenJDK is the “greenest” out of the managed languages due to that.
I'd be interested in some JVM vs .NET 6 benchmarks too, which platform to chose when.
EDIT: I know about the benchmarks and I also feel like sometimes these benchmarks are really optimized in a non-idiomatic way. I would love to know how performance idiomatic Java/.NET code is and if one is to start a new project today why one would choose the JVM over .NET or when someone would chose .NET over JVM.
I’m guessing a lot of the speedups come from getting rid of legacy cruft. With .net core/.NET 5/6 they got rid of a lot of things compared to .NET Framework 4.8 and could play with optimizations that simply weren’t doable before. That’s just me guessing, though ;)
This has happened before with .NET Framework 2, 3, 4 etc. but instead of making a .NET Framework 5 they rather made .NET Core cross-plattform and threw backwards-compatability-at-all-cost out of the window. While all .NET Framework applications (except the ones that do naughty stuff with reflection) that were compiled for .NET Framework 4.5 behave the same way on .NET 4.8, .NET (Core) got rid of this and lets developers bundle the CLR directly, giving them more leeway for incompatible changes.
They invested a lot of time adding language features with compiler and runtime support to avoid e.g. heap allocations/copying, like Span<> and friends, (readonly) ref structs, in/ref/out parameters (ref and out parameters existed before but were used a lot less in the runtime), or ValueTasks to some degree. This in turn enabled a lot of potential for optimizations in the compiler (aside from essentially writing an entirely new bytecode compiler with Roslyn and entirely new JIT with RyuJIT, throwing out the crufty old compilers), in the general runtime, and in the specific runtimes/frameworks e.g. ASP.NET. Those optimizations have to be implemented first however, and more and more get implemented with each new version.
I have a project I maintain that sees an almost 50% speedup from net48 to net5, and another 10-15% speedup from net5 to net6 (based on the time it takes to run the extensive test suite). It isn't even that compute heavy. From profiling it appears that a lot of these speedups are due to internal copies of data being avoided, and a lot of additional fast-paths in the runtime (e.g. fast-paths for byte-arrays or character-arrays as opposed to taking the generic array slow paths).
Another thing of note is that they added a lot of `bool Try*(..., out result)`-style APIs meant to avoid exceptions and the associated handling, and switched a lot of internal code to use these functions. E.g. in the reference source of the net48 runtime I think there are still a lot of instances of
try {
var number = int.Parse(value);
}
catch {
// slow path/error path
}
instead of the new-idiomatic .netcore and later style of if (!int.TryParse(value, out var number)) {
// slow path/error path
}
try-catch was/is slow-ish, and throwing exceptions is too, aside from it preventing inlining by the JIT a lot of times.And while #nullable (source annotations for what is nullable or not) and associated annotations such as MaybeNullWhen() had no direct influence on how the compiler could optimize, it probably helped people a lot writing correct code and as a side effect a lot more code became compile-time provable non-nullable which enabled further optimizations e.g. generating code that skips redundant null checks.
`Int32.TryParse` has existed since .NET Framework 2.0, which was released on February 13, 2002: https://docs.microsoft.com/en-us/dotnet/api/system.int32.try...
And even tho this particular one has existed for a long time, that doesn't mean it was used consistently in the runtime or in the popular first and third party frameworks.
I'd argue the Try*-style, while artifacts of it were present before already, only really became widely idiomatic with dotnetcore.
So in Java they just made exceptions really fast. There are lots of runtime optimizations around exceptions, for example, if you regularly parse strings that aren't numbers then the resulting exception will automatically stop having its stack trace filled out, which makes throwing drastically cheaper. The JVM can also inline the code and then convert try/catches to ordinary gotos.
In a lot of cases Try* is the outright right approach, too. Like `IDictionary.TryGetValue(key, out var value)` is better than try { var value = IDictionary.GetValue(key); } catch (KeyNotFoundException) {}` and has no race like `if (IDictionary.ContainsKey(key)) IDictionary.GetValue(key);`.
Try* functions are still free to throw in actually exceptional cases, just not on generic not-so-exceptional errors.
If you really need context, there is nothing from stopping you from implementing Try* functions in your own APIs that either have another out param for the error information, return the error information instead of a bool or use a Result<T, Err> kind of type (or a tuple), either.
More specifically though the JVM has tended to be better about optimizing naive code than .net while .net has tended to offer more tools to do your own optimizing (unsafe, simd, value types, etc). So it would be interesting to see if the performance of naive code has improved relative to Java lately
And which benchmarks games are those? If I go to to the Techempower benchmark and select only C# + Java. Java comes on top in every individual category of all the benchmarks.
I'm not claiming that Java is faster than .NET. Just that I don't believe one platform is significantly faster than the other.
[1] https://www.techempower.com/benchmarks/#section=data-r18&hw=...
Are these programs benchmarking typical idiomatic Java, or just some subset of the language?
https://benchmarksgame-team.pages.debian.net/benchmarksgame/...
c# regex redux - 1.42 seconds
java regex redux - 5.31 seconds
Ok... but looking at the code:
https://benchmarksgame-team.pages.debian.net/benchmarksgame/...
import java.io.*;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.Map.Entry;
import java.util.function.*;
import java.util.regex.*;
import static java.util.stream.Collectors.*;
...
It's only using vanilla Java features.c# ?
...
using System.Runtime.InteropServices;
...
Interesting, why does it need that? [DllImport("pcre2-8", EntryPoint = "pcre2_compile_8", CharSet = CharSet.Ansi)]
extern static IntPtr PcreCompile(string pattern, long length, uint options,
out int errorcode, out long erroroffset, IntPtr ccontext);
[DllImport("pcre2-8", EntryPoint = "pcre2_jit_compile_8", CharSet = CharSet.Ansi)]
extern static int PcreJitCompile(IntPtr code, uint options);
[DllImport("pcre2-8", EntryPoint = "pcre2_jit_match_8", CharSet = CharSet.Ansi)]
extern unsafe static int PcreJitMatch(IntPtr code, byte* subject,
long length, long startoffset, int options, IntPtr match_data, IntPtr mcontext);
[DllImport("pcre2-8", EntryPoint = "pcre2_match_data_create_8", CharSet = CharSet.Ansi)]
extern unsafe static IntPtr PcreMatchDataCreate(uint ovecsize, IntPtr mcontext);
[DllImport("pcre2-8", EntryPoint = "pcre2_get_error_message_8", CharSet = CharSet.Ansi)]
extern unsafe static int PcreGetErrorMessage(int errorcode, StringBuilder buffer, long bufflen);
[DllImport("pcre2-8", EntryPoint = "pcre2_get_ovector_pointer_8", CharSet = CharSet.Ansi)]
extern unsafe static IntPtr PcreGetOvectorPointer(IntPtr match_data);
[DllImport("pcre2-8", EntryPoint = "pcre2_substitute_8", CharSet = CharSet.Ansi)]
extern unsafe static int PcreSubstitute(IntPtr code, byte* subject,
long length, long startoffset, int options, IntPtr match_data, IntPtr mcontext,
byte* replacement, long rlength, byte* outputbuffer, out long outlength);
Aha! It's because the c# impl is really just a wrapper round a native C impl of the problem.In what world is this a useful comparison?
The fastest "real" c# solution is still faster than the java one though:
c# (real) - 3.1 seconds
https://benchmarksgame-team.pages.debian.net/benchmarksgame/...
- very naive code (shortest, most readable & easy to write code)
- idiomatic code
- optimized code without other-language-libs wrappers and without SIMD, single threaded
- optimized code without other-language-libs wrappers and without SIMD, multi-threaded
- optimized code without other-language-libs wrappers and with SIMD and/or multi-threaded
- optimized code with other-language-libs wrappers allowed and any other optimization technique2. We wouldn’t look at the benchmarks game if we thought an alternative presentation was more useful.
In this world where we also compare to C++ programs.
In this world where — as you acknowledge — we also compare a C# regex-redux program that does not use a third party library.
https://benchmarksgame-team.pages.debian.net/benchmarksgame/...
> Interesting, why does it need that?
2 out of 10 tasks regex-redux and pidigits accept third-party libraries, the other 8 out of 10 tasks do not.
If you're also separately benchmarking the same C library running on its own, then it's quite interesting to benchmark a .NET wrapper around the exact same library, as it allows you to estimate the overhead from the .NET runtime itself as separated from user code (ideally you'd try this with a bunch of different C libraries).
Of course, the program should be very very clearly labelled accordingly. Since it was just labeled as "csharpcore", then I am inclined to think the submitter was treating the benchmark as a competition.
benchmarkgame does not attempt to compare idiomatic solutions for languages, it is closer to a “what is the best you can do” benchmark
As I suspected. So of course this tells us very little about how fast idiomatic code is relative to other languages. "The best I can do" is to invoke hand optimised assembly language, but rarely is that the right choice.
A much more useful test would involve benchmarking some similar real world apps that solve the same problem.
Please show the objective rules to direct how comparison should be done when one languages "idiomatic" is not the same as some other languages "idiomatic" — to avoid you can write Java in any language.
Please show the objective rules to direct how comparison should be done when one languages "typical idiomatic" is not the same as some other languages "typical idiomatic" — to avoid you can write Java in any language.
I think there's value in benchmarks showing both the fastest you can go if you need to (specializing everything to eke out max performance), and benchmarks showing how fast you will typically go if optimizing for productivity.
Why would we think that would be similar for both you and `grumpyprole`.
https://benchmarksgame-team.pages.debian.net/benchmarksgame/...
Be aware that many implementations on benchmarksgame are much lower-level and using all kind of performance tricks than what you would normally write.
The benchmarks game puts the source code under bright light, a couple of clicks from the measurements.
That's why people have noticed differences and commented.
techempower is isolated to web framework testing as far as I know
I understand Sun ex-employees have an axe to grid with Oracle, yet no one else cared for Sun assets.
OpenJDK has the same goddamn license as the linux kernel. It is (yes, the open source codebase) developed by Oracle 98+% alone, and other vendors are just forks of this code base (including oracle jdk, which contains only trivial changes AND a paid support option, for those that need it)
You can hate Oracle as much as you want but their Java division is a surprisingly adapt and capable team, doing very great job at stewarding the language.
What in the Java world is in the same maturity tier as ASP.NET is open to opinion, but at least local Java devs seem to consider Spring or Micronaut as sane defaults, and of course modern ASP.NET runs circles around those.
https://www.techempower.com/benchmarks/#section=test&runid=9...
i.e. Its hard to read benchmarks without context of each framework shown, the compromises they have taken, how usable it actually is for building software vs just a benchmark, what shortcuts are done in the benchmark, how idiomatic is the code, etc.
https://www.techempower.com/benchmarks/#section=data-r20&hw=...
My personal experience having worked on both platforms for several years is that Java is easier to get to an acceptable performance, but the .NET runtime when you have to put the effort in has a higher upper bound of performance. It just has more tools in the CLR to work with than the JVM (e.g. value types, proper generics, spans, and more) so you can express something with a little more mechanical sympathy. Java is left with some decisions from legacy IMO that by default hurt its performance (i.e. lots of default boxing has hurt me before especially with generics). With .NET Core and future versions I think .NET is also taking up Java's default perf area as well. YMMV but if I'm worried about performance being a risk in my project .NET gives me more tools to optimize it IMO should that risk eventuate.
Lol look at the code. N-body for example, the C# is horrific C-in-C# code with a million optimizations (just read the comments lol), the Java code is idiomatic and not optimized at all.
A lot of the coding style seems optimized around copy-pasting the C code, e.g. trying to alias the Vector methods (Vector256.Create) to their instruction name (_mm256_set1_pd). That makes the code non-idiomatic, but it also doesn't really help performance, just makes the porting easier.
The F# example is on the same runtime and a better view of using the numerics directly. As a trade-off of performance, memory, and code complexity it is actually a pretty solid balance, which I wouldn't have expected.
https://benchmarksgame-team.pages.debian.net/benchmarksgame/...
I can inline C code in Ruby, does that mean Ruby is as fast as C now?
I'd much rather see a comparison of idiomatic code in different languages. When I choose a language to build something in I'm not thinking "How can I write C in this language?"...
There are many simpler implementations in all languages. I actually like that there are multiple implementations, as this lets us estimate the benefit and complexity of adopting various optimizations. Limiting the benchmark to naive implementations would penalize languages with more broad capabilities for optimization.
I'd personally prefer a benchmark limited to memory-safe implementations, though.
Look at this C# program —
https://benchmarksgame-team.pages.debian.net/benchmarksgame/...
Look at this C# program —
https://benchmarksgame-team.pages.debian.net/benchmarksgame/...
Why should C# programs be limited to what can be done in Java?
As you know simpler C# programs are shown.
(1) .NET Framework was slow and had some bad habbits (e.g. heap allocations, reflections, little optimizations, etc) ... especially the web stack. .NET Core/.NET fixes that, issue by issue. And since .NET is historically very close to the underlying platforms, we now see competitive outcomes (to e.g. Go, C++, etc).
(2) Performance = lower CPU/Memory Allocation = more throughput = lower Cloud costs. At scale, that makes a huge difference.
There's also TPC-C benchmark suites where people benchmark their own software and claim results. Not really independent journalists there.
No, they are not, but they are just a measurement tool, not a source of absolute truth. When I studied engineering at ETH we learned "Who measures measures rubbish!" ("Wer misst misst Mist!" in German). Every measurement has errors and being aware of these errors and coping with it is part of the engineering profession. The problem with programming language benchmarks is often that the goal is to win by all means; to compare as fairly and objectively as possible instead, there must be a set of suitable rules adhered to by all benchmark implementations. Such a set of rules is e.g. given for the Are-we-fast-yet suite (https://github.com/smarr/are-we-fast-yet).
For example, benchmark game allows for warmups and so does awfy. This favors jits because it allows them to warm up when they would otherwise be slower. This might give the mistaken impression that java is a great choice for command line tools due to the performance characteristics.
In contrast, most benchmarks I've seen don't use profiler guided optimizations for C or C++. Hence the subjectivity.
And the claim of only wanting idiomatic code in awfy. This is, of course, subjective as well.
The gap closed significantly with .NET core, which is why everyone was quite surprised when .NET 5 (the next iteration) had a fairly significant speedup in many scenarios.
For reference, Stack Overflow was running on .NET MVC on like 2 servers pretty recently (with some auxiliary infrastructure for CDN and search) and using MS SQL.. I think it might still be running on this setup but not 100%. Honestly I have no idea how they do it on a .NET monolith but there you go.
Low latency in a single rack can work wonders for performance. All those cloud services talk to each other over miles of cables and if you can slash latency to submillisecond regions, you get less wait time and free resources quicker. If you don't distribute your state across multiple Microservices, you can also save quite a bit of overhead.
Plus hardware is just wicked fast nowadays and SO has a model of millions of reads for a single write.
They are absolute beasts of machines. But yes, it’s incredible what you can do with just a few colocated physical servers.
It suggests to me lots of dev shops pay the IaaS or PaaS cloud tax, long before they would have hit any scaleabilty walls with a non-cloud setup.
They are measurably faster than even contemporary laptops though plus you often get ECC ram and raid disk setups and the good old Xeon didn't used to ramp up and down in speed, it just ran fast all the time. I'd still characterise that as a beast, especially on $/performance terms (although the power consumption is a worry).
Also, I hadn't twigged that “logical” cores are distinct from physical cores, which made the core count seem more impressive.
Or in my experience, most of the time a service's latency is dominated by out-of-process calls, e.g. the time taken to talk to other services over http, or to retrieve data from a data store. Speeding up the runtime is welcome, but even a massive 40% speedup of something that constitutes 10% of your total latency is ... closer to a 4% reduction in latency. Design matters more.
It's not always true that there is other work to do in the meantime. In fact in my experience, it seldom is. "you're doing programming wrong" is a very strong statement, and not one that I take seriously in this context.
Typically you "await" the external service response, so that it is not using a thread to do that, and "other work" in the form of starting to deal with other requests can happen in the meantime, thereby increasing service throughput.
But that won't speed up a given request - you can wait for an external service more efficiently, but you can't wait faster.
Services that do not depend on any other http services or any data store do happen, but they are rare in my experience (calculation engines, I suppose). So for almost every service, when thinking about response time, you have to, first and foremost, think about the latency of the data stores or upstream services.
If You want to be pedantic - and you most certainly do - then only .NET performance itself is relevant to performance of . NET itself, that's a truism, as defined.
But this narrow focus is not useful - if you want to do the job, then you have to think a bit more widely, and understand what the real problem is.
I am not saying that; you are oversimplifying into a straw man. great-grand-parent comment is where I literally put a non-zero number to how relevant language perf improvement was: https://news.ycombinator.com/item?id=29295950 and later on "Both can be relevant"; which all contradict your characterisation.
The only person who said above "it's not relevant" is you, and you also said "external services are irrelevant" - You're projecting the "it's irrelevant" statement onto me here.
But the design considerations of how and when to use external services very definitely are relevant to service latency, contrary to what you say, for reasons given multiple times above. Your current odd comments are not fact-based or interesting, so I don't think that you have anything more to add to this discussion at all.
What work? Mine bitcoins while you wait the result for an API call?
If you need to improve this, and that code is .NET, then the solution is a different design, also in .NET Code.
Back in full framework days we had to do a fair bit of optimisation to get great performance out of .NET, but as of .NET Core 3.1 the framework _just gets out the way_ - most memory dumps and profiling subsequent to that clearly pinpoint problem areas in your own app rather than being muddied by framework shennanigans.
Source: I used to work on the Platform Engineering team at Stack Overflow :)
That's surprising to read. Is that because of the sheer volume of question pages? I don't think I've ever been on an SO page that couldn't have been served straight from cache.
Though I guess it’s possible for a power distribution for page-likely-to-be-hit to still be useless for caching, because I think you could still get that distribution if 99% of hits are on nearly-unique pages; with a long enough tail, you’d still have only relatively few pages worth bothering to cache, but by far most visits are in the tail
They expanded on that functionality with span and made sure that the common libraries is implemented using this.
If you do textbook OOP development for everything, you will end up with a lot of allocations and what not, which was the case, so they went through the entire base class library and rewrote all often used methods to be faster.
they even posted a bunch of posts with all their improvement tricks: https://devblogs.microsoft.com/dotnet/performance-improvemen...
They forgot to tell the others that there is life beyond OOP and GoF design patterns.
For some niche applications (i.e. financial exchanges), .NET 5 [was/is] arguably the fastest way to implement certain ring buffer abstractions because of its interesting blend of performance and safety. There is a variant of the LMAX Disruptor developed for .NET which leverages the value semantics of the C# struct to push things beyond what the Java implementations are capable of [0].
Certainly, with enough resources and manual memory management, you could best the C# implementations using a C/C++/ASM codebase, but this is a tenuous tradeoff with practical risks that must be accounted for.
[0] https://medium.com/@ocoanet/improving-net-disruptor-performance-part-3-introducing-the-valuedisruptor-5b467730bbeSee https://www.quora.com/Is-the-Mono-CLR-really-slower-than-Cor... and http://software.rochus-keller.ch/Are-we-fast-yet_results_win... for the details.
What I am saying, I guess, is that I am not quite sure how much of your benchmark results come down to the quality of IL your custom compiler spits out.
And even though it works today, it is more of an hack than anything I would thrust to sign into production with my name on it.
It's just "compiling", not "cross-compiling"; using CLR/CIL as a language backend is an intended feature, that's why the CLR and IL are standardized in ECMA-335 and ISO 23271, and that's why it is called "common language infrastructure".
> Most people would write more or less idiomatic C#
You are welcome to write a C# version of the benchmark.
> have the "official" compiler (Roslyn)
It's not the "official" compiler, but just the C# compiler implemented by MS and community; there are a lot of other compilers too.
branch Branch optimizations
cfold Constant folding
cmov Conditional moves [arch-dependency]
deadce Dead code elimination
consprop Constant propagation
copyprop Copy propagation
fcmov Fast x86 FP compares [arch-dependency]
float32 Perform 32-bit float arithmetic using 32-bit operations
gshared Enable generic code sharing.
inline Inline method calls
intrins Intrinsic method implementations
linears Linear scan global reg allocation
leaf Leaf procedures optimizations
loop Loop related optimizations
peephole Peephole postpass
precomp Precompile all methods before executing Main
sched Instruction scheduling
shared Emit per-domain code
sse2 SSE2 instructions on x86 [arch-dependency]
tailc Tail recursion and tail callsYou made a claim. Someone disputed the validity of your evidence. And your response is “well you can rewrite/replicate my entire project if you like”.
I think most people are going to assume your claim is bullshit and move on with their lives. You made the unconventional claim so the burden of proof is on you.
My assertion is supported by sufficient evidence. The criteria of scientificity are fulfilled. You can repeat the experiment on your system yourself if you wish. Under the referenced links you will find everything necessary to do so.
Microsoft published an enormously long article detailing many of the optimizations that were done (https://devblogs.microsoft.com/dotnet/performance-improvemen...). And it is not very suprising that pure number-crunching benchmarks only using the .NET IL would not gain very much. As much as I hate to discuss what "real world" applications are, the claims Microsoft and others are focusing on are much more relevant for typical applications where .NET is used than your examples.
No one is disputing the results of your test. The question is will those results be replicated under conditions that are relevant to people writing code in a mainstream language under a much more prevalent compiler?
The answer might be yes! Everyone should always be suspicious of microbenchmarks. However people are also wise to be suspicious of benchmarks in obscure languages.
Your results introduce too many new variables for anyone to be comfortable to use it as a data point to inform their decision making.
That sounds pretty official.
Edit; will try to post some numbers when all tests succeed; it is closed source but for a large (millions LoC) codebase I think it is nice to see how it performs under the same conditions compared to our current prod.
Perhaps parent comment meant "unstable" in the sense that it turns compile time failures into runtime ones:
e.g. without reflection, if you type "customer.GetOrders()" then it either compiles or does not, whereas reflection code that finds a method called "GetOrders" can compile just fine but you won't know if it finds a method of that name or returns null, until runtime.
In practice there is though, it just depends on what you choose to take a dependency on.
For example, a few years ago the C# compiler did some lambda function optimization work. This broke someone's code because they were using reflection, and ultimately depended on how lambdas were getting optimized prior to the performance improvement in the compiler. The team by-designed that regression, since they make no guarantees that you can depend on a particular implementation detail of how the compiler optimizes things.
That said, when people use reflection in .NET, they're almost always programming against something that is stable and has likely worked the same way for a decade.
Reflection in .NET lets you dynamically invoke anything declared internal or private as well. I think it goes without saying that your code can be broken in the future if you do this.
Java never broke this weirdly though.
E.g. in Java calling a method through reflection is guaranteed by the language to work. 100% stable forever.
Reflection also allows you to call internal JVM methods. This might or might not work depending on the JVM, making reflection an unsafe feature. It's still stable on the JVM's is works on though.
Potayto, potahto I guess.
Why?
I don't see anything weird in data collected
https://docs.microsoft.com/en-us/dotnet/core/tools/telemetry...
Anyway, OP was worried about installing .NET because it has telemetry by default, meanwhile you can disable telemetry before running your war-app or just ship standalone? idk.
In other words, if you just downloaded the .NET or .NET Core runtime to host an app, there's no .NET telemetry.
As far as the .NET SDK, you can disable telemetry by setting the environment variable `DOTNET_CLI_TELEMETRY_OPTOUT` to `1` or `true`.
[0] set DOTNET_CLI_TELEMETRY_OPTOUT environment variable to 1 or true
see https://wiki.c2.com/?ComPlus
I think what happened might have been similar to what ended up happening with the .NET name later - there was a name associated with an umbrella strategy, a bunch of different technical components were under development associated with that strategy, but only some of them were released before the strategy changed again, while others were repurposed/repositioned to be part of the new strategy.
This is a common pattern with Microsoft product/feature naming and I think it's one of the reasons everyone including Microsoft developer relations people routinely comment that Microsoft "not good at naming things". It's continuing now with UWP and WinRT, where those names are actually used to refer to a bunch of different things that were once part of a now-defunct Windows strategy - some of these things are now deprecated, while others (like the WinRT core language interop model) are still the basis of most new Windows API development, but this is very confusing to developers because of their association with the abandoned overall UWP strategy
If they only had kept the way .NET Native and C++/CX exposed COM, but that would be too easy for their ways, and those tools are now gone.
It was marketing.
Then my boss reminded me that we had new hypervisors with SSD (the old one had still spinning platters) so now I'm not so sure the .net 6 upgrade really made my app faster.
> It always seemed to me that it was Microsoft's attempt to lock people into Windows forever.
I also tried installing Mono the .NET for Linux? Mono didn't help getting the .exe running.
I am only locked into Windows by this one .exe that I need to run.
It has had first-class support for Linux and MacOS for the past five years so that certainly isn't the case these days. I actually develop C# applications on Mac and run them in production on Ubuntu, no Windows involved in the toolchain anymore.
* Common Language Runtime(aka CLR, aka runtime) and its JIT compiler RyuJIT
* The C# language and it's compiler Roslyn
* The base class library and framework class libraries(aka BCL and FCL)
* ASP.NET the flagship web application framework
* F# the platforms flagship functional language
* etc etc
It's all ".NET"
nothing for common user code
why?
because benchmarking JIT code is cheating, nobody runs THE SAME code path 1_000_000 times in a row to warmup the JIT
you run something here, then there, then over there, then sometimes there, this is not JIT proof and you constantly get the JIT to do work
that's part of the reason Go became popular, on top of the single binary and the cloud native libraries story
There are scores of Windows developers who’s skills are now transferable to Linux, Apple, and Android development (albeit with some caveats).
Thank you, Microsoft, for showing me a way out.
Many Linux people like to use Python, for instance. Unfortunately, it's a slow language for all sorts of incidental but hard-to-fix reasons. This has led people to look for alternatives. I think Go is increasingly used as an alternative, but it lacks features like operator overloading which are a necessity in certain areas. There's JVM languages like Java, Kotlin and Scala as well. Nim is up-and-coming. Some people use functional languages like Haskell and Common Lisp. C++ is widely used but not much loved.
I realize of course that not all Linux users are C programmers.
Given the opportunity to transfer my C# knowledge over to Linux, I'll take it.
From my personal perspective, roughly 75% of all applicable (non-Windows-UI) .NET greenfield project go straight on Linux. The brownfield/maintenance situation is surely different. Companies are not married to the Windows stack when you get the alternative for free. And R&Ds just follow that.
.NET is on Linux and performs excellent there.
(FWIW, I spend about 40% of my working time dealing with dotnet).
Since we are on the topic of Linux so something exciting was merged for .Net6 that wasn't talked about; the new file interface with support for symbolic links. That sounds a bit absurd but it's been a long standing issue with challenges you wouldn't expect..
Definitely some room to grow in the technical respects, but the .NET ecosystem is pretty decent, as it is.
Eh?
C# and F# are fabulous languages - it's a huuuge stretch to claim that almost anything else is "better".
C# is still one of the best languages I've used, which is the reason why I've kept at it for so long - e.g. it got async/await semantics in like 2012 (just after F# did). I'm about to switch jobs to a company that uses Typescript/Node after years in .NET and I feel like I'm going to miss quite a lot of the development experience. I'm not sure which alternatives are necessarily better but again I haven't spent a significant amount of time with, for example, Golang or Scala. Swift was kind of equivalent but (AFAIK) missing some features and the vastness of the nuget package ecosystem.
Better how? There are people convinced that C is the best because it's the fastest, and if that the only thing thst matters to them, they aren't wrong.
Most desktop applications targeted at Windows are written in .NET and C++ only comes into the picture via COM/DLLs, hardly anyone writes pure Windows applications in straight C or C++, unless we are talking about games.
It is Windows/Linux that are becoming irrelevant.
Future is about browser applications/mobile applications and cloud workloads.
Yes of course there are uses for desktop computing but those are specialists.
While general public will be using phones and tablets not even owning a laptop. On the phone/tablet people don't even care what is the OS.
I already know people who don't have computers at home, only tablets/phones/gaming consoles. Normal people want to play games, message each other, no one cares about OS an Microsoft knows that.