Linux in general provides the same speed for pure CPU workloads like generating JSON or HTML responses.
Some I/O operations run about 20% better, especially for small files.
One killer for us was that the Microsoft.Data.SqlClient is 7x slower on Linux and 10x slower on Linux with Docker compared to a plain Windows VM!
That has a net 2x slowdown effect for our applications which completely wipes out the licensing cost benefit when hosted in Azure.
Other database clients have different performance characteristics. Many users have reported that PostgreSQL is consistent across Windows and Linux.
It is probably worth reporting your findings and environment here: https://github.com/dotnet/SqlClient
Although I'm not sure how well-maintained SqlClient w.r.t. such regressions as I don't use it.
Also make sure to use the latest version of .NET and note that if you give a container anemic 256MB and 1C - under high throughput it won't be able to perform as fast as the application that has an entire host to itself.
This issue has been reported years ago by multiple people and Microsoft has failed to fix it, despite at least two attempts at it.
Basically, only the original C++ clients work with decent efficiency, and the Windows client is just a wrapper around this. The portable “managed”, MARS, and async clients are all buggy (including data corruption) and slow as molasses. This isn’t because of the .NET CLR but because of O(n^2) algorithms in basic packet reassembly steps!
I’ve researched this quite a bit, and a fundamental issue I noticed was that the SQL Client dev team doesn’t test their code for performance with realistic network captures. They replay traces from disk, which is “cheating” because they never see a partial buffer like you would see on an Ethernet network where you get ~1500 bytes per packet instead of 64KB aligned(!) reads from a file.
That may be a bit of an assumption. I've been perpetually surprised by expectation-versus-reality, especially in the database world where very few people publish comparative benchmarks because of the "DeWitt clause": https://en.wikipedia.org/wiki/David_DeWitt
Additionally, a lot of modern DevOps abstractions are most decidedly not zero cost! Containers, Envoys, Ingress, API Management, etc... all add up rapidly, to the point where most applications can't utilise even 1/10th of one CPU core for a single user. The other 90% of the time is lost to networking overheads.
Similarly, the typical developers' concept of "fast" doesn't align with mine. My notion of "fast" is being able to pump nine billion bits per second through a 10 Gbps Ethernet link. I've had people argue until they're blue in the face that that is unrealistic.
It's sooooo good now. Fast, great DX, LINQ, Entity Framework, and more!
But I still come across a lot of folks that think it's still in the .NET Framework days and bound to Windows or requires paid tooling like Visual Studio.
I'm working on a large TypeScript codebase right now (Nest.js + Prisma) and it's actually really, really bad.
Primarily because Prisma generates a ton of intermediate models as output from the schema.
On the other hand, in EF you simply work with the domain model and anonymous types that you transform at the boundary.
Nest.js + Prisma ends up being far more complex than .NET web APIs + EF because of this lack or runtime types. Everything feels like a slog.
But If you knew what you were doing, for certain kinds of math heavy code, and aggressive use of low level features (like raw pointers) you could get within 10% of C++ code, with the general case being that garden variety non super optimized code being half as fast as equivalent C++ code.
I think this ratio has remained pretty consistent over the years.
Obviously as with all such benchmarks the skill of the programmer doing the implementing matters a lot. You can write inefficient clunky code in any language.