Cost of a Integer Cast in Go
boyter.org
boyter.org
You have to be really careful to ensure the numbers you're getting are really coming from the thing you're trying to benchmark, and aren't corrupted beyond recognition by your benchmarking harness.
Some of these questions would surely be trivial if I actually knew any Go, but I'm left wondering:
* What does the machine code / assembly look like for this? What does the cast compile down to?
* What's `int` an alias for? I assume 64-bit-signed-integer?
* Are integer casts checked in go? Would an overflowing cast fault?
integer casts are unchecked.
* Architecture based, so 32 or 64 bit signed integer.
* No faults. Signed become -1 and unsigned become MAX.
Edit: why I'm being downvoted: https://go.godbolt.org/z/699d7KjWr
"On x86 GNU/Linux systems the gccgo compiler is able to use a small discontiguous stack for goroutines. This permits programs to run many more goroutines, since each goroutine can use a relatively small stack. Doing this requires using the gold linker version 2.22 or later. You can either install GNU binutils 2.22 or later, or you can build gold yourself."
Considering GCC can optimize well written code to the point of saturating the target processor, given the correct target flags, I'm not entirely sure that "gc" would be "much" faster than GCCGo. I'm relatively new with Go, but equally old with GCC, esp. with g++, so assuming the optimization prowess is equally valid for GCCGo.
Last but not least, GCCGo is a part of GCC as a primary language since 4.7, which is an eternity in software terms.
[0]: https://gcc.gnu.org/git/?p=gcc.git;a=blob;f=libgo/VERSION;h=...
If the code after the cast is blocked on a memory load, then you have a lot of free instructions while the cpu is waiting for the memory load to complete. In this case it doesn't matter if the cast is free or takes a handfull of instructions.
Sometimes code becomes faster by using more instructions to make the data more compact so more of the data stays in the caches.
That said, I did like this website where you could set up JS benchmarks, they would run on your own machine and you could compare how it ran on other people's systems. It wasn't perfect, but it gave a decent indication if X was faster than Y. Of course, it's a snapshot in time, JS engines have gone through tons of optimizations over the years.
If you only call this function once in a while, then the context is more important than the function.
You can only ignore the context when you do video decoding or matrix inversion or similar "context free" long running code.
It's really important to look at the actual machine code.
> It will literally turn a "sum of i from 0 to N" into "n(n+1)/2", among others[1]
Yeah, seen that on a godbolt youtube vid. Question is, should it do this? Or should it force you to use a library, by reporting what you're trying to do and telling you there's an easier way ( "sum of 1 to n is <formula>, instead of a loop use library function 'sum1toN()" )
I think getting too clever risks hurting the user by not letting them know there's a better way.
[1] actually it seems to do a slightly different version of this to prevent risk of overflow, but same result.
Generally I think some of LLVM's optimizations are trying too hard for the amount of complexity and compilation overhead they create. All that complexity comes at the risk of bugs. With optimizations around UB, it becomes downright mind-boggling what could go wrong. But I'm not an LLVM maintainer so what the heck do I know.
That 3x overhead has nothing to do with converting types.
A CPU can't compute x += y before it has the old value of the x. This is called "data dependency chain".
Modern AMD64 CPUs have 1 cycle of latency to add integers, 3 cycles of latency to add FP32 or FP64 numbers. This means no matter what else is in the loop, a loop which increments a single float or double variable can't possibly be faster than 3 cycles / iteration.
As demonstrated by the OP, Apple M1 is very similar in that regard.
I'm not sure if hardware microcode can be smart enough to detect that and do it with register renaming and such, but a sufficiently smart compiler could. (Or realistically it would be optimized into SIMD instructions; what I said in the first paragraph is really an ad-hoc implementation of SIMD.)
Indeed, here’s an example which uses even higher latency FMA instructions to accumulate: https://stackoverflow.com/a/59495197/126995 The example uses 4 independent accumulators to saturate the throughput, instead of stalling on the latency. BTW, on most modern CPUs that code doesn’t saturate the compute throughput, the bottleneck is loads.
> a sufficiently smart compiler could
They tend to avoid doing that for floats, for accuracy reasons: float addition ain’t associative.
Indeed, unless you use -ffast-math (also known as -fidontcareaboutthe results). See https://stackoverflow.com/questions/7420665/what-does-gccs-f...
It can’t deal with the latency of ARM64 equivalent of addss/addsd due to the dependency chain. I think that’s the bottleneck of the OP’s microbenchmark.
The point of Go is that its quick to learn, quick to get stuff done, and its got a fantastic stdlib that enables you to get most modern things out-of-the box.
If someone is worrying about the cost of an integer cast in Go, then either:
(a) They are using the wrong language and should be using something more low-level (e.g. Rust, C etc.) instead.
(b) Like many who went before them, they are engaging in premature optimisation
I'd put money on most people falling head-first into (b).For example += on floats is slower than += on integers. So does casting actually take 3x as long or is that an artifact of poor test design?
We don’t know the shape or result of the filter at all from the article.
that's not even a debate. Have you done tests on this? If I remember right, once the string runs over the GC default (I think 1 GB), "+" turns from linear to exponential time. So yeah I guess for most stuff it doesn't matter, but when it does matter its a huge time difference
Not wishing to sound snarky, but is there even a valid use-case for concatenating GB+ quantities of strings (or even hundreds of MB) ?
I mean surely you'll (potentially) run into memory exhaustion issues and other fun things ?
When? Why not use ropes and avoid the copy?
Edit: nevermind, I thought i was from an array, not just incremented.
I'm not convinced finding increasingly esoteric bugs in an interviewer-selected language is an effective gauge of coding ability. I expect this is actually worse than whiteboard coding.
Programmer should be able to solve problems and apply the selected language for solving these problems as efficiently as possible.
Of course programmers should test their code themselves and minimise the bugs, but their work it not to look for them.
I have to respectfully WAT. Code review should be a part of everybody's workflow. And all programmers involved should be looking out for bugs. The best bugs are those which were never merged in the first place.
Exactly, it is one workflow. Not their whole job. Their overall job is to solve problems with code.
Ops tech 1 - "hey... my database just dropped an entire table, i lost a week of work"
Ops tech 2 - " thats a serious bug, you should escalate"
Ops tech 1- "hey you wrote this thing, it dropped my db table, i lost a week of work... "
Great and Mighty Programmer - ' - not my job, i am a programmer for you see, and looking for bugs is beneath me, some peasant task. now begone, i must solve more problems! '
Ops tech 1 "so what do we do? this doesnt work, like, at all. completely broken, not even the most rudimentary testing was done by who ever created it"
Ops tech 2 "stop using the database, we will build an excel spreadsheet on a shared network drive"
I’ve had pentesters review code looking for things like insecure hashing or encryption, or low hanging fruit like cress in the code, but I wouldn’t be inclined to leave what is essentially a QA process to a pentester.
How about: why is finding increasingly esoteric bugs in an interviewer-specified language a great way of hiring software engineers?
I don't know. You were saying it wasn't, so I wondered why.
> The filter for this is a simple review exercise. We present a small chunk of code and ask them to review it over 15 minutes pointing out any issues they see. The idea is to respect their and our time. It works pretty well and we can determine how much experience someone has by their ability to pick up the obvious vs subtle bugs.
So maybe there's more? But it's definitely a mandatory, initial part of the interview process at the very least.
I've actually done an exercise like this about a year ago, in Java, I remember I really cared about iterators at the time, and so even though it's probably not the worst thing wrong with the code I just kept coming back to these C-style for loops which I thought were reprehensible. I should ask my interviewer (whose department I now work in) what their impression was as a result of this obsession.
You lose data due to overflows which is what we expect
Seems like a reasonable question, though without more context that may or may not be true.
Produces the assembler line
0x001e 00030 (.\cast_test.go:10) ADDL CX, DX
In comparison the code
x += i
is using
0x001e 00030 (.\cast_test.go:19) ADDQ CX, DX
The results are definitely different since the ADDL is for 32 bits and ADDQ is 64 bits and that is how its achieving the cast. Not sure the intent of the benchmark (the cost of a cast) is being captured directly since we have a specialist instruction for add to do the casting. But when you think about it ADDL 0, $Val achieves a cast and its 1 instruction so its not expensive at all and there may be something better than that for explicit casts, I doubt there is worse. Its basically free, I see no performance difference between them and that fits with expectations since casting is just throwing away all the top bits.
It strikes me as so different from what the FAANGs are doing: they ask you to write trivial algorithms on the whiteboard, which apparently filters 99% of engineers.
Theoretically you can have a good candidate that doesn't know e.g. that appending to a slice passed in a function argument is a recipe for disaster. You'd show them the door over something that they could learn in a day.
Basically if you don't know something you could have learned in a day in preparation for an interview at a top paying company that in itself is a sufficient signal.
I would think in general that it’s better to select people who adapt and learn quickly rather than people who seem to know everything but you’re right: we are not fishing in the same pond as the FAANGs.
You seem to be assuming that the intended answers are language subtitles but I don't see evidence of that in the post.
Huh? This is a common pattern. (hash.Hash).Sum, strconv.AppendInt, etc.
I think you're referring to the fact that append can mutate existing memory (that is, it doesn't always allocate new memory), but in practice, this is rarely an issue, because, by convention, functions that append to a slice always return that slice.
I wouldn't expect there to be, but unless this program is only going to be running on new macs, this analysis isn't really complete.