Live data from Hacker News

Practices for writing high-performance Go

github.com

31–40 of 102 posts

Re: Practices for writing high-performance Go

#31

Earlier quoted context omitted.

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 perfo…

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…

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

Re: Practices for writing high-performance Go

#32

Earlier quoted context omitted.

Hmm, that seems a bit exaggerated. I seem to recall someone on the Go team describing the rewrite of a server that was originally in C++ and became much faster. (Largely because it started out at a heap of legacy code that was neglected for years.) That's probably pretty unusual, but the point is, just because it was written in C++ doesn't mean it's any good. Not everything gets serious attention from people who know…

You are thinking of dl.google.com. It was a Go program that replaced a very old single-threaded C++ server (using the ancient SelectServer C++ core, deprecated at that time). The thing you have to realize about Google infrastructure is it does not require vertical scalability of its service backends. It is very typical to write a program and deploy it with 100 replicas having one CPU each on 100 different machines. A…

It was even crazier: when the original download server was written, local disk was faster, mainly because the network wasn't too fast (rack locality was a concern, way back when), but also because GFS chunk servers weren't, either. At the time of the rewrite, Firehose and co. were being deployed everywhere, D did a better job at serving bytes and, later, local disk use was placed in a lower QOS level. Unless you were one of the few teams that had a good rationale for dedicated machines, if you fought for I/O time on a given disk against D, invariably you lost.

Re: Practices for writing high-performance Go

#33
post #4
post #2

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-runnin…

That is still a useful tool in Go, but you don’t need to use it as much as you’d think. The GC is pretty good at short-lived objects. The canonical implementation is sync.Pool if you don’t want to build one yourself.

Usually I use pools to avoid the allocation cost. The GC sweep is not usually my problem.

Re: Practices for writing high-performance Go

#34

Earlier quoted context omitted.

Where's the evidence that Go can't handle large amounts of concurrency?

All over my profiler. Go can deal with something embarrassingly parallel, same as any other language, but try getting it to scale up to high core counts with small RPCs is impossible, for the reasons the OP mentioned: you can't avoid its global allocator and you don't control the scheduler either. You will spend all of your CPU time in runtime.findrunnable and runtime.mallocgc.

I guarantee you the "little bit of planning" you're doing in C++ is far greater than the equivalent planning in Go to avoid the global allocator in your goroutines (i.e., allocate on the stack, use object pools, etc). I'm sure you still won't get to the same performance ceiling that C++ allows, but it doesn't mean "Go is bad at concurrency".

Re: Practices for writing high-performance Go

#35

Earlier quoted context omitted.

All over my profiler. Go can deal with something embarrassingly parallel, same as any other language, but try getting it to scale up to high core counts with small RPCs is impossible, for the reasons the OP mentioned: you can't avoid its global allocator and you don't control the scheduler either. You will spend all of your CPU time in runtime.findrunnable and runtime.mallocgc.

Is there a Github issue for this problem? If not, would you consider filing one? https://github.com/golang/go/issues It should probably explain why one cannot "avoid its global allocator" by using pools and/or stack objects.

Stack is great, then you will spend more time in runtime.morestack because the Go authors have wisely decided that we are all too dumb to request specific stack sizes and we must rely on copying and doubling stacks larger than 2KiB even if we know in advance that 2KiB is not enough. This compares poorly with native threads where the stacks are dynamically allocated one page at a time without copying and we can specify the stack size _limit_ per thread if we need to. This is another place where the Go authors just arbitrarily decided that users cannot be trusted.

sync.Pool is fun but it has known scalability limits that are yet to be addressed in a released runtime (see https://go-review.googlesource.com/c/go/+/166960/ for example). You also can't use sync.Pool for anything that you need to be non-ephemeral, like CPU-local counters, because the GC can just blow them away at any time and the finalizers run at arbitrary future times or possibly never.

Re: Practices for writing high-performance Go

#36
post #15

Earlier quoted context omitted.

"The key point here is our programmers are Googlers, they’re not researchers. They’re typically, fairly young, fresh out of school, probably learned Java, maybe learned C or C++, probably learned Python. They’re not capable of understanding a brilliant language but we want to use them to build good software. So, the language that we give them has to be easy for them to understand and easy to adopt." – Rob Pike

For a company with supposedly high hiring standards. Granted domain knowledge also matters but that isn't what the people described have. So what does Google teach them?

Everyone I met there had my respect. I find "not capable of understanding a brilliant language" to be baseless contempt and I'm surprised he didn't get more heat over it. I wouldn't work with him, nor whatever people he's trying in vain to accommodate.

Re: Practices for writing high-performance Go

#37
post #30
post #22

Earlier quoted context omitted.

You're talking a about a very specific scenario, out of the box Go is off course easier than C++ for concurrency. Who by the way runs applications on a 88 core server, why would you so they instead of splitting that into smaller chunks.

> Who by the way runs applications on a 88 core server, why would you so they instead of splitting that into smaller chunks. Graph analysis and/or routing, for one. Distributed Dijkstra is pretty impractical (the optimal lower bound on the number of messages required equals the number of edges in the graph!) For example, I don't work for Google, but I can pretty much guarantee you that an individual Google Maps routi…

I would be very, very curious if this is actually true. I agree with the intuition behind the statement but I am actually curious how it is done.

Re: Practices for writing high-performance Go

#38
post #24

Earlier quoted context omitted.

That is taken out of context. Practically all Google server code is written in C++ or, at a much smaller scale, Java. Go is used for logs analysis, internal services, monitoring and automation, etc. Nobody at Google reaches for Go when they are thinking of high performance nor would anyone try to optimize a large service in Go. They would just write it in C++ instead as soon as it starts to cost non-trivial amounts o…

High performance is a very vague terms now days, there are company that have Google scale problems that don't use C++ as main language ( Nerflix Uber ect ) any modern runtime can do "high performance" especially for backend applications. YouTube primary language is Python for instance.

Netflix OK but Uber? They did 4 billions rides in 2017. Thats naivly about 130 rides per second with nice parallelism due to locality of the physical rider. That's not an insane amount of data crunching.

Re: Practices for writing high-performance Go

#39

Earlier quoted context omitted.

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…

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, gi…

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.

"You don't pay for it," should really be, you == the-CPU doesn't pay for it. On the other hand you == the-programmer has to think about it all the time. For some domains/contexts, yes this is actually very desirable. In that case C++ becomes a performance/optimization Swiss army knife.

Other people might want it dialed in for, "You don't have to think too much about it, until it's time to optimize, and then you can't optimize all the way, but you get enough of the way there."

Re: Practices for writing high-performance Go

#40
post #12

Earlier quoted context omitted.

Concurrency is a bit easier in Go than in C++.

Disagree. I can write a server in C++ that, with careful thought and a bit of planning, will be able to exploit the resources of the kind of 88-core dual-socket machines that are mainstream today. No amount of planning will allow me to write a Go program that does the same thing on the same machine. Small amounts of concurrency might seem easy in Go but lots of concurrency is hard.

"I can write a server in C++ that, with careful thought and a bit of planning, will be able to exploit the resources of the kind of 88-core dual-socket machines that are mainstream today."

This is downvoted grey as I write this, and as someone who has been called a "Go shill" on occasion... it's exactly right. Go is a decent language for writing code in a fairly straightforward manner and getting pretty good performance out of it, but if you need to squeeze every bit of performance out of your hardware, it's a bad choice. (I can give you choices that are worse by an order of magnitude, or even more in some cases, but it's still a bad choice.) You have a fairly smooth optimization ride up to 1.5-2x slower than C for most use cases, a few pathological edge cases where it's grossly worse (many clustered around these "every drop of performance" problems!) and a few where it'll reach parity, and then you're going to hit a brick wall.

An 88 core system is probably not impossible to sensibly use with Go, but you're going to be more constrained. I'd imagine it can probably serve web requests really well, but it's more likely to hit pathological cases if you hammer certain global resources.

Arguably, precisely part of the point of Go was that C++ makes you pay for that level of performance in code complexity and cognitive overhead all the time, even when you don't remotely need it. (I can't prove this, but I'd guess the median "cloud service" is grotesquely overprovisioned on the smallest AWS instance. The "cloud services" that leap to mind are things like the AWS auth servers or Netflix content servers or the Google crawling or indexing servers, but while those are huge and important, they're also in many dimensions the exceptions. A good chunk of the popularity of "serverless" is probably a result of this.) When you do need every bit of performance, though, the list of viable options is short.

Post reply on HN