Live data from Hacker News

Making a Go program faster with a one-character change

hmarr.com

161–170 of 249 posts

Re: Making a Go program faster with a one-character change

#162
post #159

Earlier quoted context omitted.

> you can't const-cast the templated const away. That seems like a good thing. If you're handed a map to const values you can't just go "imma gunna mutate them anyway".

Yup, it's definitely a bit of a code smell if you do. The issue is more the reverse, though - I can't make a mutable map, then hand it by pointer/reference to something that says it wants an immutable map later.

Not necessarily a bad thing either, things can get odd if you're not the only owner and it's mutated under you unexpectedly.

Re: Making a Go program faster with a one-character change

#164
post #42

Earlier quoted context omitted.

IMHO, implicit allocations is a bit of a red herring. Yes, in C/C++ heap allocations are proportionately pretty expensive, but I've seen Java programs have just ridiculous amounts of implicit allocations but there really isn't much of a problem. But allocations aren't the same as copies, and the argument for reference semantics has always been that implicit copies are problematic. In your std::string example, having…

> Yes, in C/C++ heap allocations are proportionately pretty expensive, but I've seen Java programs have just ridiculous amounts of implicit allocations but there really isn't much of a problem. Java programs make "ridiculous amounts of implicit allocations" because allocations are cheap in Java. And they need to be cheap because Java doesn't have value semantics so it leans hard on escape analysis + cheap allocations…

A long long time ago Rust was a GC language.

Re: Making a Go program faster with a one-character change

#165
post #139

> I did consider two other approaches: Changing Ruleset from being []Rule to []*Rule, which would mean we no longer need to explicitly take a reference to the rule. Returning a Rule rather than a *Rule. This would still copy the Rule, but it should stay on the stack instead of moving to the heap. > However, both of these would have resulted in a breaking change as this method is part of the public API. The problem wi…

Sometimes it doesn't matter if a public API is incorrect, because it's set in stone for whatever reason, and you just need to fix the problem internally.

This is why I like https://github.com/openrewrite so much. One gets to tell users how to rewrite code automatically. It makes refactoring almost as easy as in a mono repo.

Re: Making a Go program faster with a one-character change

#166
post #159

Earlier quoted context omitted.

Yup, it's definitely a bit of a code smell if you do. The issue is more the reverse, though - I can't make a mutable map, then hand it by pointer/reference to something that says it wants an immutable map later.

Not necessarily a bad thing either, things can get odd if you're not the only owner and it's mutated under you unexpectedly.

[deleted]

Re: Making a Go program faster with a one-character change

#167

Earlier quoted context omitted.

Go compiler is garbage by the design. A 20 year old C compiler does not have this prob. This is also why Go have declined so much during the last couple of years. The benefits of Go have not increased and most of the quirks are still there. Like the error handling, the naive compiler and the syntax sugar that somewhat hides the diff between pointers and direct heap allocs. -1

I work on a code base that is a mixture of Go and C. It's IO, CPU and Memory hungry, and it's distributed. C is fast because it's close to how CPU and memory actually work. Go gives you 95+% of that plus easy to learn, easy to use language. A new person could start contributing useful features and bug fixes immediately. A senior person could get C-level performance. More and more of our code is moved from C to Go, wi…

> C is fast because it's close to how CPU and memory actually work.

Out-of-order execution, cache hierarchies, branch prediction, virtual memory, pipelining, vector instructions, ILP, NUMA are all pretty transparent to the C spec.

Trying to accommodat hardware quirks with C feels like blackbox engineering. It's certainly better than with managed languages but still....

Re: Making a Go program faster with a one-character change

#168

The deeper lesson here is "don't use pointers unless you're sure you need them". I've seen quite a few people use pointers for no reason in particular, or there's simply the assumption it's faster (and have done this myself, too), but it puts a lot more pressure on the GC than simple local stack variables. Of course sometimes pointers are faster, or much more convenient. But as a rule of thumb: don't use pointers unl…

Eh, I've waffled a couple of times between "pass values by default" and "pass pointers by default". Ultimately, I don't think there's a really good answer except to understand escape analysis and get comfortable with the escape analyzer. Notably, "using pointers" doesn't inherently put pressure on the GC, but rather allocations put pressure on the GC and there are plenty of instances where pointers don't put pressure on the GC at all (notably, if you're passing data into a function by pointer, it's not going to allocate, but it may if you're returning a pointer to "a stack-allocated value").

Notably, if you're defaulting to values, you may still have a bunch of allocations when you try to implement interfaces, which usually (always?) requires implicitly boxing values; however, if you pass a pointer into a function that takes an interface, I don't think it gets moved to the heap (but I'm not sure, which is why Go programmers need to be comfortable with profiling the escape analyzer and also why it would be great if Go actually had explicit semantics for allocations).

Re: Making a Go program faster with a one-character change

#169
post #146

Earlier quoted context omitted.

Agreed. Could you Imagine a Java where you have a `Map` and a `MutableMap` and that's what you put at your API? I'd make it SO much clearer how safe any individual API is to call.

Scala has had this for ages. You can have it today. Even in Java either through the Google collection library or through a library that mimics fp style programming. The name eludes me for the moment.

Guava, but it's not fully typesafe.

Mutable is not part of the Java meta-language used for typing.

ImmutableMap implements Map

https://guava.dev/releases/23.0/api/docs/com/google/common/c...

and throws exceptions on mutation.

https://guava.dev/releases/23.0/api/docs/src-html/com/google...

Re: Making a Go program faster with a one-character change

#170

The deeper lesson here is "don't use pointers unless you're sure you need them". I've seen quite a few people use pointers for no reason in particular, or there's simply the assumption it's faster (and have done this myself, too), but it puts a lot more pressure on the GC than simple local stack variables. Of course sometimes pointers are faster, or much more convenient. But as a rule of thumb: don't use pointers unl…

> but it puts a lot more pressure on the GC than simple local stack variables. Do you have evidence for this claim? AFAIK the Go compiler does escape analysis, and allocates pointers that don't escape on the stack.

This is true, but it's hard to tell if a pointer escapes or not without actually profiling. That said, I don't think the answer is to avoid pointers, but rather to get comfortable with profiling the escape analyzer. By default, I just stick to the subset of Go which I know won't escape--functions can take pointer parameters, but I'm very careful about returning pointers to data that would otherwise be stack-allocated (even though it's not especially idiomatic, I'll often prefer mutating an `out T` parameter rather than returning a `T` because I know the former will not allocate).
Post reply on HN