Live data from Hacker News

Generics can make your Go code slower

planetscale.com

261–270 of 418 posts

Re: Generics can make your Go code slower

#261
post #46

I'd argue that golang is inherently not a systems language, with its mandatory GC managed memory. I think it's a poor choice for anything performance or memory sensitive, especially a database. I know people would disagree (hence all the DBs written in golang these days, and Java before it), but I think C/C++/Rust/D are all superior for that kind of application. All of which is to say, I don't think it matters. Use t…

I agree. golang is not really a system programming. It's more like java, a language for applications.

It does have one niche, that it includes most if not everything you need run a network-based service(or micro-service), e.g http,even https, dns...are baked in. You no longer need to install openssl on windows for example, in golang one binary will include all of those(with CGO disabled too).

I do system programming in c and c++, maybe rust later when I have time to grasp that, there is no way for using Go there.

For network related applications, Go thus far is my favorite, nothing beat it, one binary has its all, can't be easier to upgrade in the field too.

Re: Generics can make your Go code slower

#263
post #114

Earlier quoted context omitted.

Indeed. What really kills is extremely high allocation rates and extremely high garbage production. I've seen internal numbers from $megacorp that show that trashy C++ programs (high allocation + deallocation rates) look pretty much the same to CPUs as trashy Java programs, but are far worse in terms of memory fragmentation. Trashy C++ programs can end up spending 20+% of their execution time in malloc/free. That's a…

> I will admit that the programming culture is different for many GC'd languages' communities, sometimes encouraging a very trashy programming style, which contributes to the perception that GC itself is the problem For some languages (I’m looking at you, Java), there’s not much of a way to program that doesn't generate a bunch of garbage, because only primitives are treated as value types, and for Objects, heap allo…

It is indeed a huge problem of Java, that it often makes it difficult to avoid generating garbage. However, one still can reduce it a lot if trying hard. And be it by reimplementing selected parts of the standard libraries.

But the job of avoiding garbage is much easier in Go :)

Re: Generics can make your Go code slower

#264

Earlier quoted context omitted.

I have a slightly contrary opinion. Systems software is a very large umbrella, and much under that umbrella is not encumbered by a garbage collector whatsoever. (To add insult to injury, the term's definition isn't even broadly agreed upon, similar to the definition of a "high-level language".) Yes, there are some systems applications where a GC can be a hindrance in practice, but these days I'm not even sure it's a…

The article is from a database company, so I'll assume that approximates the scope. My scope for the GC discussion would include other parts that could be considered similar software: cluster-control plane (Kubernetes), other databases, and possibly the first level of API services to implement a service like an internal users/profiles or auth endpoints. The tricky thing is GC works most of the time, but if you are wo…

I think I toggled with the GC for less than a week in my eight years experience including some systems stuff - maybe this is true at FANG scale but not for me!

Re: Generics can make your Go code slower

#265

Earlier quoted context omitted.

It was not a joke. Let's look at a common example: you want to return two different types of errors and have the caller distinguish between them. Let me show it to you in rust and go. Rust: #[derive(Error, Debug)] pub enum MyErrors { #[error("NotFound: {0}") NotFound(String), #[error("Internal error")] Internal(#[source] anyhow::Error), } The equivalent go would be something like: type NotFoundErr struct { msg string…

I dont think you realize how ridiculous this comment is. Youre comparing 10 lines of Go, with 200 of Rust: https://github.com/dtolnay/thiserror/blob/master/src/lib.rs

What's the difference between importing some hundred's of lines from thiserror in rust vs importing the "error" package in go?

If the difference is just "It's okay to use stdlib code, but not dependencies", then go's a great language by that metric.

I don't think that's what matters at all. What matters is the abstraction that the programmer deals with.

In go, the abstraction I deal with, as a programmer, is what I showed above.

In rust, the abstraction I deal with is also what I showed above. One of those things is simpler.

Further, the abstraction in go is leakier. My function returns a 'error' interface even if it can only be those two concrete types, so the caller of my function has to step into my function, reason through it to figure out all the error variants, and then check them with 'errors.Is' or such, and changes to what variants are returned aren't statically checked.

In rust, I can look at the type-signature, write a 'match' statement, and never have to manually figure out all the variants returned, since the compiler just knows.

My point here is that what matters is the abstraction that programmers interact with. Third party libraries are bad when they give a leaky abstraction that causes the programmer pain. Standard libraries likewise.

In this case, the abstraction available in rust is cleaner and requires less code for me, the user of the package, so I don't think the lines of code used to build that abstraction matter.

Why do you see this as something that matters?

Re: Generics can make your Go code slower

#266

Earlier quoted context omitted.

The article is from a database company, so I'll assume that approximates the scope. My scope for the GC discussion would include other parts that could be considered similar software: cluster-control plane (Kubernetes), other databases, and possibly the first level of API services to implement a service like an internal users/profiles or auth endpoints. The tricky thing is GC works most of the time, but if you are wo…

> A good portion of production outages are likely related to cascading failures due to too long GC pauses, and a good portion of developer time is spent testing and tuning GC parameters After 14 years in JVM dev in areas where latency and reliability are business critical, I disagree. Yes, excessive GC stop the world pauses can cause latency spikes, and excessive GC time is bad, and yes, when a new GC algorithm is re…

> I've never had to debug corrupted memory

You're lucky! When OpenJDK was still closed-sourced Hotspot from Sun, we have chased bugs that Sun confirmed was a defect on how Hotspot handle memory (and this is on a ECC'd system of course), although these days I can't remind of anything remotely related.

> or how a use after free bug let people exfiltrate data.

Technically you're just outsourcing it :)

Re: Generics can make your Go code slower

#267

Earlier quoted context omitted.

I dont think you realize how ridiculous this comment is. Youre comparing 10 lines of Go, with 200 of Rust: https://github.com/dtolnay/thiserror/blob/master/src/lib.rs

What's the difference between importing some hundred's of lines from thiserror in rust vs importing the "error" package in go? If the difference is just "It's okay to use stdlib code, but not dependencies", then go's a great language by that metric. I don't think that's what matters at all. What matters is the abstraction that the programmer deals with. In go, the abstraction I deal with, as a programmer, is what I s…

> What's the difference between importing some hundred's of lines from thiserror in rust vs importing the "error" package in go?

Again, you're comparing apples and oranges. It seems you didn't see my previous example, here it is again:

    type errorString string

    func (e errorString) Error() string {
       return string(e)
    }

Re: Generics can make your Go code slower

#268

Earlier quoted context omitted.

Assuming your generic functions take _pointers_ to Components as input, full monomorphization does not occur and you're suffering a performance hit similar in magnitude, if not strictly greater empirically, to interface "dereferences". On this basis, I don't believe your generic implementation is as faster than an interface implementation as you claim.

You're right, here's what one of my hot loops look like: func (cc *ComponentContainer[T]) ForEach(f func(*Component[T])) { for _, page := range cc.pool.pages { for i := range page { if page[i].IsActive() { f(&page[i]) } } } } Still, the interface approach is a total nightmare from a readability + runtime error perspective so I won't be going back & will just hope for some performance freebies in 1.19 or later :^)

Just to clarify things here: a pointer receiver on a generic type isn't what was covered in the article. That was covering if your `T` is a pointer type.

If T here is a value-type, you're probably getting monomorphization (at the cost of copying the value all over, and possible benefit of inlining to reduce that copying).

Re: Generics can make your Go code slower

#269

Earlier quoted context omitted.

What's the difference between importing some hundred's of lines from thiserror in rust vs importing the "error" package in go? If the difference is just "It's okay to use stdlib code, but not dependencies", then go's a great language by that metric. I don't think that's what matters at all. What matters is the abstraction that the programmer deals with. In go, the abstraction I deal with, as a programmer, is what I s…

> What's the difference between importing some hundred's of lines from thiserror in rust vs importing the "error" package in go? Again, you're comparing apples and oranges. It seems you didn't see my previous example, here it is again: type errorString string func (e errorString) Error() string { return string(e) }

I addressed that with the start of my comment: "Let's look at a common example: you want to return two different types of errors and have the caller distinguish between them"

Yes, your example implements the error interface, but it's not realistic. There is real go code that does that, but that's the reason I have to do string-matching type error handling in go, and frankly it's an argument against the language that its error handling is such a mess that some people see that as reasonable.

Having code that does

    return errorString("You have to do string matching on me internal error")
is something you can do, sure, but it's not good code. Idiomatically in go, it would also be "errors.New", not this errorString type you made.

In rust, that would also be just as easy since it would be:

    bail!("oh no internal error")
using anyhow, which is the de-facto standard way to do crappy string-style erroring, like the above.

But that's not what I want to talk about since that sort of error handling isn't interesting in either language, and isn't where they differ.

Re: Generics can make your Go code slower

#270
post #46

I'd argue that golang is inherently not a systems language, with its mandatory GC managed memory. I think it's a poor choice for anything performance or memory sensitive, especially a database. I know people would disagree (hence all the DBs written in golang these days, and Java before it), but I think C/C++/Rust/D are all superior for that kind of application. All of which is to say, I don't think it matters. Use t…

We have been using golang for a long time without generic overhead. Why is now that it’s added we stop caring about performance. Is this the same HN that complains about memory bloat in every JavaScript thread?
Post reply on HN