Live data from Hacker News

Print(“lol”) doubled the speed of my Go function

medium.com

111–120 of 130 posts

Re: Print(“lol”) doubled the speed of my Go function

#111
I was curious what this strange assembly language was, as it looked like neither Arm nor x86.

Apparently the Go toolchain has its own assembly language which partially abstracts away some architectural differences: https://go.dev/doc/asm

I wonder what the advantages are? It feels like as soon as you move away from the basics, the architecture-specific differences will negate most usefulness of the abstraction.

Re: Print(“lol”) doubled the speed of my Go function

#112

I was curious what this strange assembly language was, as it looked like neither Arm nor x86. Apparently the Go toolchain has its own assembly language which partially abstracts away some architectural differences: https://go.dev/doc/asm I wonder what the advantages are? It feels like as soon as you move away from the basics, the architecture-specific differences will negate most usefulness of the abstraction.

I guess it's for historical reasons. As the document you linked states, "The assembler is based on the input style of the Plan 9 assemblers". It's important to know that at least two of the "founding fathers" of Go (Rob Pike and Ken Thompson) are ex-Bell Labs guys and were involved with Plan 9. The Plan 9 compiler toolchain was available, they were familiar with it, so that's what they used for Go. Some parts of the toolchain (the linker, I think) have been swapped out in the meantime, but the assembly format has stayed.

EDIT: found the document talking about changing the linker: https://docs.google.com/document/d/1D13QhciikbdLtaI67U6Ble5d... . Favorite quote:

> The original linker was also simpler than it is now and its implementation fit in one Turing award winner’s head, so there’s little abstraction or modularity. Unfortunately, as the linker grew and evolved, it retained its lack of structure, and our sole Turing award winner retired.

...which is referring to Ken Thompson I guess.

Re: Print(“lol”) doubled the speed of my Go function

#113
post #87

Earlier quoted context omitted.

....And then feel OK resorting to ChatGPT for the explanation. Seriously that threw me, and maybe it makes sense in this context but it seems strange for someone with such an apparent depth of technical knowledge leaning on an LLM for anything.

It seems they didn’t want to bother coming up with their own explanation, but then why not just link to https://en.wikipedia.org/wiki/Branch_predictor ?

pasting LLM answers is sort of a tongue-in-cheek rhetorical flourish these days

Re: Print(“lol”) doubled the speed of my Go function

#114

Most languages have a `max` function, so the core of the loop could be written with just something like: `maxV = max(maxV, v)` That could be entirely branchless, right?

A max function still compiles down to some kind of branch

Re: Print(“lol”) doubled the speed of my Go function

#115
post #47
post #38

Kind of tangential, but who are these people who are so comfortable with disassembling a high level language binary, reading assembly, and then making statements about branch prediction and other such low level esoterica? I've only ever meet people like that maybe two or thee times in my career, and yet it seems like every other blog post I read in certain language circles everyone is some kind of ASM and Reverse Eng…

They're a dying breed. We're forgetting how to look under the hood and understand "why something works". Case in point, I'm slowly being replaced by Salesforce muppets for all my projects at work. They're little code monkeys with amazon ebook type knowledge, projects cost 20x more and I look like the mad scientist for speaking the truth. The products are worse in every possible metrics, I'm not crazy. The politics at…

> Cabinet designers are being replaced by Ikea flat pack artists in the software world

That analogy doesn't really work if the new projects "cost 20x more" upfront, unless you mean long-term associated costs.

Re: Print(“lol”) doubled the speed of my Go function

#116
post #44
post #41

Earlier quoted context omitted.

This feels semi-normal to me... just have the curiosity to ask "why?" and the bias-to-action to move to "I'm going to find out". You encounter far far more dead-ends than anyone ever says, and every unsolved mystery is a mild nerd snipe, an open case, that years from now you'll see someone else explain something you realise it answers that question from years prior. For me, the hard bit is not over-indexing on this..…

https://imgs.xkcd.com/comics/ten_thousand.png for those who haven't heard of it.

I always felt this one was a bit smug when applied to occupations (which it often is). People paid to do a job should know the basics of that occupation.

Re: Print(“lol”) doubled the speed of my Go function

#117
post #57

Earlier quoted context omitted.

But why does the print statement matter in how the compiler decides this? In both cases, the execution still depends on the vMax value calculated in the previous iteration.

The version with the print statement requires the branch because it's conditionally executed; the 'continue' statement in the if-block will skip it if the loop finds a new maximum. Since the function-with-if version requires a branch, there's no advantage (regardless of predictability) in using a conditional move for the assignment to maximum.

So basically the print prevents an optimization, that so happens to hinder performance in this particular case?

Re: Print(“lol”) doubled the speed of my Go function

#118
post #38

Kind of tangential, but who are these people who are so comfortable with disassembling a high level language binary, reading assembly, and then making statements about branch prediction and other such low level esoterica? I've only ever meet people like that maybe two or thee times in my career, and yet it seems like every other blog post I read in certain language circles everyone is some kind of ASM and Reverse Eng…

I think it depends on what niche you're in. I'm in the storage business and while not everyone, but quite a few of the people in my group would not be afraid of running a disaseembler, or talk about branch prediction, or unaligned integer access. `likely` and `unlikely` are very common in the code, and so on. AFAIK linux kernel people are very similar, and I assume a few other niches of system programming as well.

Re: Print(“lol”) doubled the speed of my Go function

#119
The explanation is not convincing.

My guess is some kind of measurement error or one of the "load bearing nop" phenomena. By that I mean the alignment of instructions (esp branch targets?) can dramatically affect performance and compilers apparently have rather simplistic models for this or don't consider it at all.

Re: Print(“lol”) doubled the speed of my Go function

#120
post #59

Earlier quoted context omitted.

"Conditional move is slow" is not the right takeaway. (In fact, conditional move can be much /faster/ than a branch in certain circumstances (e.g. hard-to-predict branches, like binary search).) The reason why this code is slow is because the conditional move is on the critical path for resolving a loop-carried data dependency. In other words, to execute the assignment `maxV = (v if (v > maxV) else maxV)`, the CPU ha…

How exactly does that change anything? It's being run on an increasing array, so the data is assigned every loop. Surely the comparison creates a dependency on the value of the variable which was assigned in the last loop.

For a strictly increasing array, assuming the branch predictor always predicts correctly (guesses taken), the CPU does not have to wait for the test to resolve (v > maxV) before it executes the assignment (maxV = v). So effectively the CPU is just running

  maxV = v
  maxV = v
  maxV = v
  maxV = v
over and over (with tests running in parallel as well, but those matter less, because they are mostly just "checking" whether the branch predictor guessed correctly.).

Then, in that above sequence, there is no /read/ of maxV after each write, so there is no true data dependency. (We don't need to wait for the last write to finish before we write again, unlike the read after write case.)

Post reply on HN