C
C#10mo ago
Omar

❔ Why is C# so slow on Linux? How can I speed it up?

Just "Hello World" take almost three seconds to execute.
$ time dotnet run --no-build --property:Configuration=Release
Hello, World!

real 0m2.798s
user 0m3.404s
sys 0m0.576s
$ time dotnet run --no-build --property:Configuration=Release
Hello, World!

real 0m2.798s
user 0m3.404s
sys 0m0.576s
What can I do to speed it up?
65 Replies
Omar
Omar10mo ago
On the same hardware, C++ Hello world takes only a few milliseconds:
$ time ./a.out
Hello, world!

real 0m0.006s
user 0m0.005s
sys 0m0.002s
$ time ./a.out
Hello, world!

real 0m0.006s
user 0m0.005s
sys 0m0.002s
`
x0rld
x0rld10mo ago
compile in native if you want to compare with c++
Omar
Omar10mo ago
How do I do that? I don't see any options on dotnet built that look like they change the target.
x0rld
x0rld10mo ago
add <PublishAot>true</PublishAot> in your csproj and do a dotnet publish the compilation will be longer
Omar
Omar10mo ago
Looks like ARM isn't supported.
$ dotnet build
MSBuild version 17.7.1+971bf70db for .NET
Determining projects to restore...
/home/pi/hello/hello.csproj : error NU1101: Unable to find package runtime.linux-arm.Microsoft.DotNet.ILCompiler. No packages exist with this id in source(s): nuget.org
Failed to restore /home/pi/hello/hello.csproj (in 1.6 sec).

Build FAILED.

/home/pi/hello/hello.csproj : error NU1101: Unable to find package runtime.linux-arm.Microsoft.DotNet.ILCompiler. No packages exist with this id in source(s): nuget.org
0 Warning(s)
1 Error(s)

Time Elapsed 00:00:05.75
$ dotnet build
MSBuild version 17.7.1+971bf70db for .NET
Determining projects to restore...
/home/pi/hello/hello.csproj : error NU1101: Unable to find package runtime.linux-arm.Microsoft.DotNet.ILCompiler. No packages exist with this id in source(s): nuget.org
Failed to restore /home/pi/hello/hello.csproj (in 1.6 sec).

Build FAILED.

/home/pi/hello/hello.csproj : error NU1101: Unable to find package runtime.linux-arm.Microsoft.DotNet.ILCompiler. No packages exist with this id in source(s): nuget.org
0 Warning(s)
1 Error(s)

Time Elapsed 00:00:05.75
Omar
Omar10mo ago
GitHub
Support for ARM32 in NativeAOT · Issue #833 · dotnet/runtimelab
Is there any plans to support ARM32? Basically so that you from a PC running Linux(amd64) could do: dotnet publish -r linux-arm -c release Or could you already today compile applications for ARM32 ...
Omar
Omar10mo ago
Looks like the issue has been open for a couple of years, and the one guy working on it gave up this past April.
x0rld
x0rld10mo ago
oh what are you to be on 32 bit ?
reflectronic
reflectronic10mo ago
have you tried running the C# output directly instead of going through dotnet run which is going to result in a lot of overhead
x0rld
x0rld10mo ago
oh catderp /usr/bin/ld: cannot find -lstdc++: No such file or directory
Omar
Omar10mo ago
32 bits, yeah. I think most people run 32-bit on Raspberry Pi
x0rld
x0rld10mo ago
oh yeah on raspi
reflectronic
reflectronic10mo ago
you should prefer not to for one it will make .NET quite a bit faster
Omar
Omar10mo ago
running directly helps, but sitll about 50 times slower than C++:
$ time ./hello
Hello, World!

real 0m0.256s
user 0m0.182s
sys 0m0.031s
$ time ./hello
Hello, World!

real 0m0.256s
user 0m0.182s
sys 0m0.031s
Are you sure? On 64-bit Intel, it's even slower!
mike@barney:~/hello$ time dotnet run
Hello, World!

real 0m5.952s
user 0m7.891s
sys 0m0.623s
mike@barney:~/hello$ uname -a
Linux barney 5.15.0-76-generic #83-Ubuntu SMP Thu Jun 15 19:16:32 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux
mike@barney:~/hello$ time dotnet run
Hello, World!

real 0m5.952s
user 0m7.891s
sys 0m0.623s
mike@barney:~/hello$ uname -a
Linux barney 5.15.0-76-generic #83-Ubuntu SMP Thu Jun 15 19:16:32 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux
reflectronic
reflectronic10mo ago
if it took 5 seconds tha is almost cerainly because it was building it
PS D:\source\Scratchpads\Scratchpad> Measure-Command { dotnet run --no-build -c Release }
Milliseconds : 875

PS D:\source\Scratchpads\Scratchpad> Measure-Command { dotnet .\bin\Release\net8.0\Scratchpad.dll }
Milliseconds : 115

PS D:\source\Scratchpads\Scratchpad> Measure-Command { .\bin\Release\net8.0\win-x64\publish\Scratchpad.exe }
Milliseconds : 35
PS D:\source\Scratchpads\Scratchpad> Measure-Command { dotnet run --no-build -c Release }
Milliseconds : 875

PS D:\source\Scratchpads\Scratchpad> Measure-Command { dotnet .\bin\Release\net8.0\Scratchpad.dll }
Milliseconds : 115

PS D:\source\Scratchpads\Scratchpad> Measure-Command { .\bin\Release\net8.0\win-x64\publish\Scratchpad.exe }
Milliseconds : 35
the last one is native AOT
Omar
Omar10mo ago
Oh, good catch. with --no-build it is "only" 1775 milliseconds.
reflectronic
reflectronic10mo ago
to be clear, everthing you are seeing is expected
Omar
Omar10mo ago
Expected to be slow? LOL
reflectronic
reflectronic10mo ago
yes if 115 milliseconds to run a hello world is not fast enough then yeah that's what you get with .NET and, yes, dotnet SDK adds a lot of overhead
x0rld
x0rld10mo ago
for example on ubuntu
reflectronic
reflectronic10mo ago
if you need instant startup then switch to a 64-bit operating system and use native AOT the 32-bit targets are also not very well optimized especially 32-bit arm, it is missing a lot of huge JIT enhancements
x0rld
x0rld10mo ago
ubuntu x64 without aot
Omar
Omar10mo ago
What other disappointments are "expected"?
reflectronic
reflectronic10mo ago
you are free to use C++ if shaving off 100 milliseconds matters more to you than having C#
Kouhai /人◕ ‿‿ ◕人\
It's also just 100 milliseconds of start up.....
mg
mg10mo ago
/tmp/helloworld
❯ time ./bin/Release/net7.0/helloworld
Hello, World!
./bin/Release/net7.0/helloworld 0.04s user 0.00s system 54% cpu 0.065 total
/tmp/helloworld
❯ time ./bin/Release/net7.0/helloworld
Hello, World!
./bin/Release/net7.0/helloworld 0.04s user 0.00s system 54% cpu 0.065 total
fast enough for me if how many milliseconds it takes to run hello world is your primary factor in choosing a programming language then yeah i'd probably recommend C or assembly
Omar
Omar10mo ago
So, it's expected that it's slow, and tough shit if you don't like it? It's like the software industry is giving up on itself.
mg
mg10mo ago
do you write software professionally or as a hobbyist
Omar
Omar10mo ago
Yep, since 1985.
mg
mg10mo ago
it takes my shit like 3 minutes to get published to azure, so i really couldn't care less if the application takes 100ms to start instead of 50
Omar
Omar10mo ago
I'm not normally ocncerned iwth startup, but when it's so long that it dominates processing time on a small workloads. In actuality, I have a system that runs on some input. Beforehand, we don't know if the input is large or small. If it's large, great -- a 2-seconds startup doesn't matter much becuase processing takes a while. If it's small, then processing takes far less time than startup and we actually end up falling behind.
mg
mg10mo ago
now that 3 minutes is frustrating, but i don't go around calling programming languages bad and disappointing because they don't run hello world fast enough why is your program restarting for every input
Omar
Omar10mo ago
I think the service script is spawning dotnet run, so it'll certainly help to directly run the image. The startup time is still a bit of a concern. I thiiiiink I've done the math right, but we'd need it to be less than 75 milliseconds to not be an issue.
mg
mg10mo ago
well my hello world ran in 65 ¯\_(ツ)_/¯ im sure you could get it faster with native aot or you could refactor whatever your use case is so the program doesn't have to run once for every input i personally would not have .NET as my first choice for a component of a shell script. i'd either write the entire script as a .NET program or use another lang
Omar
Omar10mo ago
Sure, if refactoring large systems was free, we'd already have done that. Fact is, we're in the midst of rearchitecting a large legacy system. Maybe one day it'll be a full-blown service, but until then we run a process against available input in batches. Shell script?
mg
mg10mo ago
whatever kind of script you're running
Omar
Omar10mo ago
Maybe you wouldn't, but I don't see how that addresses this problem.
mg
mg10mo ago
idk what the "problem" is at this point. that .NET's perf disappoints you? you can look into native aot to make the startup time faster
Omar
Omar10mo ago
The problem is that there's a significant cost to starting up a dotnet app. Sorry, I thought I had made that clear before. Maybe another problem is that you're apathetic to the issue.
mg
mg10mo ago
/tmp/helloworld
❯ time ./bin/Release/net7.0/linux-x64/helloworld
Hello, World!
./bin/Release/net7.0/linux-x64/helloworld 0.01s user 0.02s system 56% cpu 0.057 total
/tmp/helloworld
❯ time ./bin/Release/net7.0/linux-x64/helloworld
Hello, World!
./bin/Release/net7.0/linux-x64/helloworld 0.01s user 0.02s system 56% cpu 0.057 total
here's hello world with native aot i personally do not find myself or hear about others repeatedly invoking .net apps in scripts. that's why i'm apathetic you're using a tool that's not optimized for your use case and then complaining that the industry is in decline when you get unsatisfactory performance
reflectronic
reflectronic10mo ago
i mean .NET startup has never been faster than it is in 2023 .NET has existed since 2002
Omar
Omar10mo ago
I'm wondering if the industry is in decline because people expect bad performance, and expect that very common platforms are unsupported.
reflectronic
reflectronic10mo ago
this is one of the tradeoffs of this technology and it always has been
mg
mg10mo ago
yeah that too, i'm not even mentioning the fact that even without native aot, the performance is to your standards "bad performance" is subjective and .net was windows only until .net core if you want blazing fast performance and code that's portable to every platform, write in C
reflectronic
reflectronic10mo ago
this is not a novel issue
Kouhai /人◕ ‿‿ ◕人\
C# is pretty performant
Omar
Omar10mo ago
to my standards?
mg
mg10mo ago
75ms right
reflectronic
reflectronic10mo ago
i am not sure why you are acing like this is something outrageous in 2023
mg
mg10mo ago
my hello world ran in 65 without native aot, 10 with. obviously there'll be a hardware difference, but i doubt native aot will be over 75 on your hardware
Omar
Omar10mo ago
Yep, 75 ms. It's almost twice that.
mg
mg10mo ago
with native aot?
Omar
Omar10mo ago
native aot doesn't work on my primary OS. I mentioned this above.
mg
mg10mo ago
sorry, must have missed it not sure what you want to hear at this point
reflectronic
reflectronic10mo ago
i mean i am sorry that 32-bit is not a focus anymore especially Arm32 which newer processors do not support at all and embedded is not a strong point for .NET so you can imagine why their priorities are Arm64 and x64 which are ubiquitous in client and server scenarios
Omar
Omar10mo ago
AOT is fast enough, but it would only help for about 30% of our fleet.
mg
mg10mo ago
all i can really recommend without AOT is to make sure you're running the executable directly and not going through dotnet run like was mentioned already
/tmp/helloworld
❯ time dotnet run --no-build -c Release
Hello, World!
dotnet run --no-build -c Release 0.36s user 0.07s system 76% cpu 0.571 total

/tmp/helloworld
❯ time ./bin/Release/net7.0/helloworld
Hello, World!
./bin/Release/net7.0/helloworld 0.02s user 0.02s system 58% cpu 0.061 total
/tmp/helloworld
❯ time dotnet run --no-build -c Release
Hello, World!
dotnet run --no-build -c Release 0.36s user 0.07s system 76% cpu 0.571 total

/tmp/helloworld
❯ time ./bin/Release/net7.0/helloworld
Hello, World!
./bin/Release/net7.0/helloworld 0.02s user 0.02s system 58% cpu 0.061 total
Omar
Omar10mo ago
It'll probably be another 6 months before we have a chance at upgrading the fleet, but I have a few things to try until then so thanks for that.
mg
mg10mo ago
i'm sure there are more in-depth improvements to perf that you can make, but i'm not aware of them
reflectronic
reflectronic10mo ago
presumably your application is more complex than hello world have you tried using ReadyToRun
Aaron
Aaron10mo ago
ReadyToRun might help, but you start running into then technical limitations of needing to start up a runtime it is inherently going to take more time to start than C++, which has minimal initialization to do in comparison
jcotton42
jcotton4210mo ago
Expected to start up slower because of JIT overhead Once the app has warmed up, there will not be much difference For things like desktop and server apps, the little extra startup time is not an issue And no one is apathetic to that issue, otherwise there wouldn't be AOT or ReadyToRun
LPeter1997
LPeter199710mo ago
Apparently you have been a professional since 1985 yet you are profiling a Hello World application lol
Omar
Omar10mo ago
Yep, because startup of even a trivial app was taking more than two seconds. Profiling my own app, in its short-run case, takes just as long.
Accord
Accord9mo ago
Was this issue resolved? If so, run /close - otherwise I will mark this as stale and this post will be archived until there is new activity.