Hacker News new | ask | show | jobs
by jjjeii3 1949 days ago
GO promised to be very fast, but if you check the actual benchmarks:

https://www.techempower.com/benchmarks/#section=test&runid=8...

High-level C# (asp.net) is almost twice as fast as GO in the benchmark... Rust is also in the top 3.

So, why should I use GO instead of C#, if ASP.NET/C# is so blazing fast and requires a lot less lines of code to achieve the same? -> It makes absolutely sense to switch to C# or Rust.

9 comments

Don't look at that kind of benchmark, most cheat, and notice how latency is bad, because C# is not native code, so the JIT needs warmup just like java, wich introduces hicups and non-deterministic performance

So these perfect situations where you only do the same thing and call same code over and over never happen; never!

Anyways, GO isn't popular for its RAW perf, GO is popular because:

- Simple language

- Native code

- GC

- Cross compilation

- Fast iteration (build/rebuild/deploy)

- Relatively tiny, statically compiled executable

> - Simple language

So simple it can't do Object.keys()

Don't get me wrong, I enjoy writing code in Go, Rust, and TS. But my god, Go literally is a pain in the ass every time I want to do something that isn't a map reduce problem. And even for map reduce problems it's annoying due to lack of generics.

Maybe generics will finally give Go something that every other language has. A strong, useful, standard library for collections.

I agree with you, i'm not using Go myself, i tried it many times but i always hit a roadblock, it's interesting because i hit the same kind of issues with ZIG

Simple on paper, but what's the experience as a result? some are okay with it, some love the restrictions and limitations, but i personally don't enjoy them

But again, i can understand the choices and the benefits

Neither Go nor Zig are spelled ALLCAPS
I mean those benchmarks are mostly web server related. And it's pretty common in web servers to trigger the same code path over and over again all the time. There's a reason why people are not rewriting their java web server application in something native to get more performance out of it.
These benchmarks also in no way represent actual webapps. Have you seen this code? Have you ever written a webapp where not using hardcoded, static headers like Content-Length was a bottleneck?
the plaintext c# one actually only sets a content length manually since without it it would be a streaming response (Transfer-Encoding: chunked), which would not make the server slower, but the client, because of buffers (also as far as I know the Content-Length needs to be set in this benchmark). btw. go does not do that when calling w.Write without wrapping the responsewriter inside a buffer (which is probably also the case why it is slower, since it needs to know the size of the written response before it goes over the network and btw. only go-std is slow because it's written for simple use cases, there are others like fasthttp who do stuff a little bit more like c# and thus are faster (even inside ;-) go-std never cared about being the fastest.)
> the JIT needs warmup just like java

You can use ReadyToRun[0] to pre-JIT portions of the code for faster startup. Full AOT compilation is in the works too[1] albeit it's still experimental.

[0]: https://docs.microsoft.com/en-us/dotnet/core/deploying/ready...

[1]: https://github.com/dotnet/runtimelab/tree/feature/NativeAOT

I think you're missing the point. The point is that you can get all those properties in Rust without the performance hit. (Except for, well, GC, obviously)
Rust does not build fast. It’s more comparable to C++ if not worse.
that depends on your dependencies I believe. If the people who keep including serde into their crates would stop everyone would have faster compile times.

Stop using serde. please.

Dependencies make this much worse, but Rust itself is not really very fast at being compiled.
The problem with rust is the language is kind of scary to learn, once you get past the learning curve, it is okay i guess

And on top of that iteration time is hurt because of both the language, the compiler perf, and the lifetime problems that you need to constantly think about

The benefits of using rust on other hand is non-negligible, i wish the team would focus on solving that kind of immediate problems

Lack of garbage is a pro, not a con.
> the JIT needs warmup just like java

That doesn't sound right. .Net has good support for caching of generated native code. It's well ahead of Java in this regard.

Did you even read the article? That's exactly what their code is doing. It's a giant LRU cache on a very hot path.
Performance is rarely the top priority for a software engineering project. If you task a whole team to switch from Go to Rust or C# purely because “Rust goes brrr” you’re going to have a bad time.

I use Go because its decently fast, I like the syntax, has a great stdlib, and I feel like it’s very easy to share a Go codebase with other engineers.

One of the key things I like about Go, despite shortcomings, is your last point. Our team effortlessly shares Go code, and its tooling makes it so any of us at any time can easily compile and run projects without hassles or hang ups.

The other day we reviewed some oldish and complex code and it was so plain to see what it was doing and get back into the flow of that logic. I don’t find this happens with other languages as much.

I’m not saying Go is the right tool everywhere for everyone. I do love that about it though.

Definitely agree with this. Go is one of the only languages I've worked in where I can jump into _any_ project and it feels familiar, I just have to dig around to get into the specifics.

I have never had that same familiarity from Python, Ruby, JavaScript. I think there's something to be said about that in Go's favor.

While I agree with you on the familiarity across different projects (and I applaud ideas like gofmt) - it's also very verbose code (due to the simplicity of the language).

That makes it very hard to read go code and build a mental model in your head.

> it's also very verbose code (due to the simplicity of the language). That makes it very hard to read go code and build a mental model in your head.

Verbose does not equal complex usually.

Something that's a single method call on a built-in type may be 3 lines in Go. This doesn't make it hard to understand or keep in your head. "This iterates over a map" is the same regardless of the lines of code. You can very quickly skim verbose code, especially in a language like Go, where idioms and conventions are identical across many code bases.

Agreed, somewhere else in this thread someone complained that Go doesn't have an object iterator function and I really don't understand how k, v := range <map> isn't an intuitive approach, especially when range is so common in Go. Sure, it might be two more lines of code but it's not like it any more difficult to reason about or mentally track.
I agree that Go is a little bit more verbose, but I usually consider that an example of "explicit is better than implicit" and I like that Go shows me the internals a lot more than other languages. The one thing that really drew me to Golang over any other was that it was extremely _easy_ for me to build a mental model of fairly complex code.

Go's interfaces in particular feel like a really easy way to build clean, composable abstractions that make sense and hide complexity in the right way. The single-function-interface pattern is really one of my favorite things about Go lately.

We might want to highlight this more often that not.

Code legibility is starting to become a primary if not the primary factor for a lot of software these days, because that's what allows it to be maintained.

I think more analysis should be done on why this is for go. Java and C# should be similarly legible, they are not complex languages either. It's possible that go encourages certain paradigms.

This sounds great but thinking of my system at work, it´s not the language that is the problem. It´s all the moving parts that you need to get to play together nicely before you can even start to develop. Front end, back end, database, nginx proxies, email platforms.
Go has won here for us as well because it forces you to write very dumb code, resulting in exposing the logic of how systems interface in a very dumb but easy to understand way.

It may take some scanning to go through all the dumb code, but you typically won't get fooled by abstractions. Go offers so little in terms of abstractions that you can usually see right through them.

This won't be everyone's experience, but it has been mine very consistently across internal and 3rd party code.

Performance is a cost concern; if I can pay for less server resources then I can either pay my programmers more or hire more programmers.
Databases and traffic is going to have your lunch anyway. Good developers cost a good deal more than extra infrastructure. Adding people to help you cut cloud cost is easier and more predictable than re-implementing.
Bad developers will cost you more.
Discord also runs in cloud. They could save $$$ by just going to the colo instead
I doubt that, with that many users they would probably need to build their own stuff, around the globe. their servers have such a low latency, that this would be impossible by just going "colo".

peope who say that they can just save money in their colo are probably never even working in the scale of discord, which is fine.

Then you’d probably be shocked to find out that they already do that for voice servers. I’m talking about the other part which doesn’t have to have many geo distributed locations only a couple
I think that the argument here is that the c# environment is much bigger and that the programming level is at a higher level. That these are things that are often made at a sacrifice for performance but that that's not the case here, so why use Go at all
I like Go for all these reasons too, and the fact that vim and a Makefile are all we need to be very productive.
I am not saying Go is faster or anything I am just saying that take the grain of salt with reading the benchmarks as they are on same level as googling for "best microwave oven in 2021" where you know it is better not to even start reading the results as they will be full of paid "fake" reviews. Not to even start about microwave oven brand fanboys.

You have to understand that the programming language is a product in same way as anything else and there are influencers, marketing bribes, paid advertisements, forum/hn/fb/... paid spammers, etc.

Just remember what spam spike the Rust (I am again not talking about rust as a language... just about the noise everyone made) had here when it emerged, they threw the shadow on the Nigerian spam, oh the amount of noise from Rust-Distributed-Evangelist-Task-Force (they will probably downvote me to hell for mentioning this :D), I had a lot of fun reading HN at that time, the most used word was 'and', the second was 'rust', it was so exaggerated that it was like watching Monty Python.

In today world the only review, or if you want, benchmark, is the one you make. Nothing else counts and if you feel that C# is faster then enjoy using it.

And quite frankly, who cares about speed today (except some very strange people like me), people are using very strange languages and want to run them on backend. And they actually do. Ignore the benchmarks, no one cares (except those that would like to advertise themself like "faster than C" :D). Just threw in some more vapor on the cloud and you have "fixed" the speed issues. /s

Have you seen the kind of C# code they write to compete in Tech Empower? It's nowhere near what one would write on a daily basis: https://github.com/TechEmpower/FrameworkBenchmarks/blob/mast...

Perhaps Go folks should just use inline assembly to demonstrate how useless these benchmarks are.

This 1000x times. Should be posted every time someone links to TechEmpower benchmark results. And especially for the most contrived benchmarks like "Plaintext" and "JSON serialization", which are basically echo server competitions that have nothing to do with real applications.
That's nowhere near as bad as inline assembly. It might not be the usual way to write a web app, but it's normal C# with common libraries. Any C# dev could use that as an example if they ran into a major performance issue.

These benchmarks aren't intended to show the performance of everyday code. They show what experts can achieve on each platform. That's why they take pull requests instead of writing the code themselves from tutorials.

That's true. .NET Core high performance optimization looks more approachable.

I'd still rather see the popular way of doing things be the benchmark.

Most code is not CPU-bound, and for code that is, a hot spot is all the usually needs to be optimized. Today .Net Core is remarkably performant, and the fact that just by writing code carefully, or using Span<> etc., you can get close to bare-metal perf with w/o resorting to FFI to unsafe code is a good thing. This is much easier than trying to keep your friendly Rust borrow-checker happy even for moderately complex data-structures with varying lifetimes.
When you put it that way .NET Core runtime does seem to offer great performance at low cost.
btw. these are the platform benchmarks of c# (only kestrel + manually writing the response, basically no framework code) the real ones are in benchmarks (aspcore-mw), which just use a middleware and are also really really fast. btw. golang's std http server is just really really slow. fasthttp solves that and is basically 6x faster in some real code cases.
If performance is the top priority and time to market can take a back seat, I skip Go and choose Rust (or other known-fast languages).

However, time to market is almost always a priority. Unless my margins are razor-thin, spending twice as much on servers for shorter development cycles is a net win in most cases.

Go is as fast as dot net in the same benchmark you posted: https://www.techempower.com/benchmarks/#hw=ph&test=plaintext

aspcore 7,016,966

gnet 7,010,982

People share results of benchmark where some languages are missing.

It wasn't that long ago that compariing .NET to Go was insane: Go was far faster but now it's a toss-up in some cases. Go still wins on small size of binary but in cloud workloads does it really matter if my lambda in go is half the memory and half the execution time when the "bigger, slower" C# version is still under 128MB and executing only a few milliseconds slower? In cloud cost terms the difference is small enough not to matter.
It tooks years and a lot of work for them to make it on part or faster, Go did not focus on performance recently. Also you should take benchmark with a grain of salt, every recent language can be fast nowdays.
Why? Well, for one, Go produces a statically linked binary and C# requires a runtime be installed.

If we’ve learned nothing from Java and Python, it’s that managing runtimes and environment can be a huge pain point.

EDIT: swipe keyboard put ringtone instead of runtime and I missed it. Corrected.

>C# requires a ringtone be installed.

This confused the hell out of me until I realized it's supposed to be runtime.

On the other hand, would you be surprised to learn the .NET runtime includes a ringtone?
That's probably why it confused me so much; I couldn't rule out that interpretation reliably enough to interpret it as a typo.
Thanks. I assumed it was an allusion to the .Net runtime having a bunch of unnecessary crap built into it, including a ringtone for some arcane reason.
Autocorrect: turning simple, obvious mistakes into opaque, irrecoverable mistakes.

It's never been clear to me why someone would want this.

> Go produces a statically linked binary and C# requires a runtime be installed

Not true since .NET Core 3.

$ dotnet publish --self-contained --runtime osx.11.0-x64 -p:PublishSingleFile=true

Not disagreeing with you, but just to be pedantic:

In .NET Core 3, you can build and distribute a single-file executable which, when run, will actually unpack an executable and a ton of DLLs into a temp directory, and run it from there.

In .NET 5 they changed it, so that when you build a "single-file" executable, you actually get an executable plus a few DLLs, which must be distributed together.

With C# and Java, we have learned that time and battle tested VMs can remove terrible pain points in proper hands.
To be fair, I have found Java’s JRE to be relatively painless. It’s fairly simple to have multiple installed and choose which one to use as a command line option.

Python OTOH...

One of the things to consider when comparing C# and Go is how it does parallelism. In C#, the new async/await mechanism can go horribly wrong if the programmer doesn't use it correctly. This can cause threads to start up, which is in fact slower than older .NET tech that had larger starting thread pools. You can view this with PerfView to see if it's your load issue.

In Go, the scheduler is smart enough to stop something even when it's not blocking on IO. This makes it more robust.

Of course, use what you need for when you need it. Rust definitely shines in some circumstances.

> In C#, the new async/await mechanism […]

C# grew the `await` operator in C# 5.0, released in August of 2012. Go altogether hit 1.0 on 2012-03-28. If the one is new, the other is new.

They key takeaway really is that for high performance, the way you think about memory layout, data structures, memory management, etc. all start to really matter. It is possible to write programs with memory pools to reuse data structures as much as possible without having to defer the work to the GC, which can then introduce performance spikes.

That said, C# and JVM are both mature and have decades of GC improvements, which could explain why golang is falling behind. Time will tell.