Live data from Hacker News

Making a Go program faster with a one-character change

hmarr.com

171–180 of 249 posts

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

#171
post #141

Earlier quoted context omitted.

That's not a deeper lesson. A deeper lesson is to understand where the pointer points to and then decide accordingly.

That is pretty much what I said, except phrased different.

It sounded to me like you were saying "avoid pointers by default" (bad advice IMO) rather than "if it matters, verify whether the pointer escapes" (good advice IMO).

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

#172

> You can see these decisions being made by passing -gcflags=-m to go build: That's a very nice feature! I wonder if compilers for other languages have something similar.

Does anyone with VS Code or vim plugin development experience know how hard it would be to call this behind the scenes and highlight allocation points in the editor?

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

#173

Earlier quoted context omitted.

> As far as I know, Java's (default) runtime gives cheap allocations at the cost of long GC pause times. "long GC pause times" is kind of vague, so I guess you could be correct, but in practice there's a LOT of different ways the memory management can be handled, many of which are deemed "pauseless GC" (though the term is somewhat misleading). My statement was considering that reality though. While not true for some…

> in practice there's a LOT of different ways the memory management can be handled, many of which are deemed "pauseless GC" (though the term is somewhat misleading). Yes, but I'm pretty sure those "pauseless GC" schemes impose other tradeoffs. > My statement was considering that reality though. While not true for some use cases, in the vast majority of cases, the runtime optimizes the allocations more than sufficient…

In hindsight, I think I chose how to present this poorly, because yes, in this case, the allocation is what is killing the performance. I look at it, and I just see unnecessary implied behaviour creating a performance problem. Usually it isn't the allocations themselves that kill you, but it certainly is the case here.

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

#174
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.

That only matters if you're storing it. The big issue is 99% of code out there uses mutable template types for containers, and if you ever declare a container that doesn't have a mutable template type, you stub your toe as your new container isn't compatible with anyone else's code. You can't even easily copy your way out.

  std::unordered_map m;
  m["foo"] = "bar";
  std::unordered_map m2 = m;
doesn't compile.

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

#175

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.

In general this doesn't work, the history rule says mutable types are not proper subtypes of immutable ones (and the converse is obvious). If you want to capture mutability in your type system, it needs to be orthogonal to subtyping (like C/C++ const).

Not quite.

Conceptually, you can have constness in your subtype-system (as long as you are sticking with interfaces (methods), as Liskov's subtyping model does, and aren't inherting potentially-mutable fields).

MutableMap and ImmutableMap are both subtypes of a hypothetical ReadableMap. ImmutableMap is the same as ReadableMap, but has an informal contract that subclasses shouldn't add mutability.

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

#176

Earlier quoted context omitted.

In general this doesn't work, the history rule says mutable types are not proper subtypes of immutable ones (and the converse is obvious). If you want to capture mutability in your type system, it needs to be orthogonal to subtyping (like C/C++ const).

> the history rule I'm unfamiliar with this rule (and not finding anything good to google). Can you elaborate? I can't really think of a scenario where an immutable datastructure isn't a subset of actions against a mutable datastructure.

I had to look it up too, it apparently is a constraint for subtypes defined in Liskovs substitution principle [1]. From Wikipedia:

> History constraint (the "history rule"). Objects are regarded as being modifiable only through their methods (encapsulation). Because subtypes may introduce methods that are not present in the supertype, the introduction of these methods may allow state changes in the subtype that are not permissible in the supertype. The history constraint prohibits this. It was the novel element introduced by Liskov and Wing. A violation of this constraint can be exemplified by defining a mutable point as a subtype of an immutable point. This is a violation of the history constraint, because in the history of the immutable point, the state is always the same after creation, so it cannot include the history of a mutable point in general. Fields added to the subtype may however be safely modified because they are not observable through the supertype methods. Thus, one can define a circle with immutable center and mutable radius as a subtype of an immutable point without violating the history constraint.

[1]: https://en.wikipedia.org/wiki/Liskov_substitution_principle#...

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

#177

Earlier quoted context omitted.

> The performance issue here is not value semantics There's no performance cost to value semantics, so of course not. > The copy is cheap. The lifetime tracking is not because it forces a heap allocation and creates additional GC pressure. In fact, assuming Rule is small, if Match returned by value, the code would be similarly as fast. I'm referring more to how this stuff seeps in without the programmer realizing it.…

Sorry, because of value semantics. (Also, you wrote "performance costs of inopportune value semantics". So the correction is annoying; you know what I meant.) This stuff seeps in without the programmer realizing it because Go made a deliberate design decision to have automatic lifetime management. In other words, this is a feature of the language and not a bug. The only way for this to not seep in is if Go forced pro…

Sorry for being annoying. I wasn't trying to be.

I would disagree about this stuff seeping in because of automatic lifetime management. The equivalent code in Java, Smalltalk, etc., would either not have the performance problem or it would be much more obvious that the code wasn't as efficient as it could be.

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

#178
post #165
post #139

Earlier quoted context omitted.

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.

This looks super interesting, but as far as I can tell they don’t go into how to inform downstream users to add your recipes to their maven/gradle configuration when you make a breaking change.

In my head, the ideal flow would be an annotation on your library function/class which triggers an IntelliJ suggestion for downstream users affected by your breaking change to run the automated refactor for them. Kinda like a more helpful @Deprecated.

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

#179
post #33

Earlier quoted context omitted.

> I've only ever found myself missing value semantics when I use languages with implicit reference semantics. Oh, I miss it every time. ;-) I will say though that some newer languages seem to have a confused idea about how to offer mixed semantics. A bunch of them tie semantics to types. The ideal interface can vary by usage context. It's hard enough getting the semantics right as the callee (as opposed to caller), l…

> I will say though that some newer languages seem to have a confused idea about how to offer mixed semantics. A bunch of them tie semantics to types. Curious about what you mean here. This sounds like C#'s class/struct distinction to me.

That's exactly the example I was thinking of.

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

#180

So, this is very basic Go design and you could write something about how it works in C and Go and why a older lang like C don't have this prob but then at the end of the day the Go fanclub will down vote the hell out you no matter what.

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

Can you show any examples of "go compiler being garbage"? In my experience, it often generates much smarter code than C# or Java.
Post reply on HN