
Practices for writing high-performance Go - ingve
https://github.com/dgryski/go-perfbook
======
legohead
Thanks for this, I just started getting into and writing Go code.

I have a question that I don't remember being answered in any tutorial I've
done so far. I've written a lot of C code and I typically make memory managed
lists, so if I need a new common object I grab one from the list to avoid
free/malloc as much as possible. Does Go do this automatically, or should I
still do this on my own? I'm writing a long-running server, not a utility or
something short lived.

~~~
shereadsthenews
The Go authors have steadfastly refused to offer users any means of writing a
CPU-local freelist, even though the utility of such things is obvious and
evidenced by the fact that they use such things all over the runtime package.
They just think that _you_ are too stupid to be allowed to do such a thing.
Honestly with the attitude that the Go authors treat their users I don't know
why anyone tries to write high performing code in that language. C++ is there,
after all.

~~~
stcredzero
_Honestly with the attitude that the Go authors treat their users I don 't
know why anyone tries to write high performing code in that language. C++ is
there, after all._

I work in C++. I'm sometimes surprised at the way C++ treats its users. There
seems to be a culture of pre-optimization. It's hard to write the most
abstract application code without constantly thinking about performance under
the surface. That's baked right in. Concepts which are used everyday by
programmers can barely be cleanly summarized in a paragraph, even by well
respected luminaries of the field. Trivia you "just have to remember,"
impinges on almost every single function or method you write.

That said, there's also a lot of awesome things in C++. It just represents a
particular set of cost/benefit dials. Golang represents another.

~~~
MaxBarraclough
C++ is unique in that it aims to be feature-rich, while preserving at much as
possible the property of _if you don 't use it, you don't pay for it_.

It breaks its own rule occasionally -- RTTI, exceptions, [0] standard library
machinery with always-on thread safety -- but the C++ folks go to extreme
lengths in the name of performance.

There's no small irony in the way embedded folks write off C++ as too
heavyweight, given that C++ tortures itself in the name of not forcing bloat
upon the programmer.

[0] [https://llvm.org/docs/CodingStandards.html#do-not-use-
rtti-o...](https://llvm.org/docs/CodingStandards.html#do-not-use-rtti-or-
exceptions)

~~~
Const-me
> it aims to be feature-rich

Visual basic had a Replace() function back in 1998, to replace substrings. C++
now has many amazingly advanced high-level features, but still no built-in way
to replace a substring. I don’t think I needed to write my own string
replacing function in any other language I used (I’ve been programming for
living since 2000).

I like C++ and use it a lot. But these seemingly small issues with its
standard library escalate quickly. String handling, IO, localization, date &
time, multithreading before C++ 11, many standard collections, and other parts
are just not good enough. I pretty much stopped writing complete apps in C++,
nowadays I’m using C++ for dll/so components I consume from higher-level
languages like C#, Python or golang. And when I do, I often choose to ignore
large parts of the standard library in favor of alternatives from atl, eastl,
or my own ones.

~~~
neop1x
String operations of C look so much cleaner and complete than those
"algorithmic and templating" ones of C++. And C++ can quickly become difficult
to read after overloading various operators on classes or after using fancier
less-known features. I personally find C++ too complex and confusing. I know
Google dictates some reasonable subset of C++ for Fuchia and that is good. Btw
also the dependency management is sometimes really pain with C/C++ - then they
had to use crazy compilation tools like GN or Basel to get it compile in
various environments and platforms... Programming in go/c# allows to quickly
focus to get the work done.

~~~
Const-me
> so much cleaner and complete than those "algorithmic and templating" ones of
> C++

That's C++ strings, too: [https://docs.microsoft.com/en-us/cpp/atl-mfc-
shared/referenc...](https://docs.microsoft.com/en-us/cpp/atl-mfc-
shared/reference/cstringt-class?view=vs-2019)

Not only they have better API (replace, tokenize, implicit cast to const
pointers), these strings are often faster. That particular class is Windows-
only, but nothing prevented C++ standard folks to come up with conceptually
similar cross-platform stuff. BTW, that CString class predates C++ standard
library by many years.

> And C++ can quickly become difficult to read after overloading various
> operators on classes or after using fancier less-known features.

Yes, and I saw quite a lot of code like that.

But in other cases these features help with readability. I often code math-
heavy stuff in C++ processing vectors, matrices, quaternions, complex numbers,
etc. The ability to implement custom math operators on these structures IMO
helps with readability.

What doesn't help is the ability to abuse them, like C++ iostreams do with
`operator <<` everywhere.

Not just operators, it's generally too easy to abuse features of the
languages, writing code that's very hard to work with. Unfortunately, not
doing that requires lots of experience with the language.

I'm not planning to switch due to the good parts. First-party SIMD intrinsics
support. Trivial interop with C and C++ libraries: hard requirements like OS
kernel APIs and GPU APIs, industry standards like libpng, or just very nice to
have like Eigen. Very small runtime allows to build dynamic libraries, consume
them from anywhere, and not worry about binary size or runtime dependencies.
Also tools like debuggers and profilers are very good.

But when performance is less critical, I'm more productive using other,
higher-level languages.

------
jnordwick
Almost none of this is about Go. 80% down they mention some basic stuff about
GC, but nothing very specific about the Go GC. Then after that it is back to
very basic language-agnostic optimization ideas.

I was hoping for more insights about Go specifically.

~~~
Cthulhu_
I've done a Go course a while ago; interestingly enough, at least the first
day, it wasn't about Go at all. The trainer basically stated that if you know
any curly braces language, you can write Go.

And I think that's the point. The language doesn't get in your way, and it's
not any specific language feature that will make your code go fast or slow -
unless you abuse it, or use it when you don't need to.

I mean if you're happy with C then by all means; Go isn't claiming to be
faster than other languages, not this close to the metal. It's mainly aimed to
take away some of the mental overhead you get with C and similar languages.
And compile times.

------
mnutt
The "How to Optimize" section is worth reading even if you don't write any Go.

------
alexk
This looks very useful, I'd definitely buy a book on the subject. Does anyone
know about more Go-specific performance book/articles out there? Please share!

~~~
val_deleplace
This draft: [https://github.com/dgryski/go-
perfbook/blob/master/TODO](https://github.com/dgryski/go-
perfbook/blob/master/TODO) This short list: [https://github.com/enocom/gopher-
reading-list#performance](https://github.com/enocom/gopher-reading-
list#performance) This article: [https://medium.com/@val_deleplace/go-code-
refactoring-the-23...](https://medium.com/@val_deleplace/go-code-refactoring-
the-23x-performance-hunt-156746b522f7)

------
libguy
any thoughts on whether to use values or pointers to large structs?

in practice I haven’t seen any major performance overhead when passing values
about.

~~~
sagichmal
You should use pointers if you need to mutate the content of the struct, and
values otherwise.

Almost all types that could hold a lot of data have reference semantics, so if
you have a struct with a large slice, or a large map, all of the actual data
is going to be on the heap, and not affect copy performance.

The only time you might want to consider using a pointer to improve copy
performance is if you have lots of non-heap-allocated data in your struct, and
in practice the only way that happens is via large arrays. That is, e.g.
`[8192]string`, and _not_ simply `[]string` (which is an efficient slice). But
you should never do it preemptively, you should always benchmark both styles
and only switch to pointers once you have proof it's meaningfully affecting
performance.

