4 ms·
It seems like it is a suboptimal choice to use a garbage collected language when trying to optimize for throughput. What made you decide to use Go instead of a
by beebmam 4y ago
It seems like it is a suboptimal choice to use a garbage collected language when trying to optimize for throughput. What made you decide to use Go instead of a language like Rust or C++?
- deleted 4y ago[deleted]
- deleted 4y ago[deleted]
- richieartoul 4y agoThis is a controversial opinion, but I’ve been generally unimpressed with the performance of real world systems written in Rust or C++. While the theoretical performance ceiling is higher, I’ve personally observed that it usually takes several times longer to write the systems in Rust/C++ and often the “naive” Rust implementation is less performant than the “naive” go implementation. There are obviously cases where using Rust or C++ makes a lot of sense, but I prefer Go. I can get a performant prototype up and running quickly, the “critical path” can usually be micro optimized to within +/- 10% of Rust if you know what you’re doing, and by the time the Rust implementation matures the Go implementation has already had time to iterate repeatedly on key architectural, data structure, and algorithmic choices based on real world usage. That’s just my 2cents though and doesn’t reflect the views of all the people I work with.
- purplerabbit 4y agoVery astute observation. Although it's a bit painful to admit, as Rust scratches a perfectionist itch which Go absolutely does not. I've read a bit about algorithm-driven performance gains vs. hardware-driven performance gains... this seems like an argument for a third category: DX-driven performance gains.
- the_duke 4y agoThese systems usually spend a lot of time waiting for network/disk IO, which means there is plenty of CPU time available to do the computations or GC runs, and the added memory pressure isn't so significant. The Go runtime is also finely tuned for exactly these kinds of workloads, with language level insight into blocking operations. If we take the Tokio async runtime for Rust: it's not exactly the fastest runtime out there, comparatively new, you need to carefully avoid introducing blocking calls that stall the worker threads, there is no language level integration, and Rusts trait system currently often requires boxing of futures. Combine all of that and you often end up with code that isn't noticeably faster than Go in practice. One counterpoint though: I've found that Rust code takes longer to write, but it's significantly easier to refactor, thanks to the much stronger type system.
- jerryjerryjerry 4y agoGood points on performance side, but IMHO one of the pain points is still the memory management. Go GC can do some of the work, however it can be hard to do real-time tracking and management while this is exactly what db kernel needs.
- adgjlsfhk1 4y agoGC isn't necessarily higher cost than not doing GC. garbage collectors are allowed to free objects when they want to which can be significantly faster than freeing in an arbitrary order as happens with manual memory management.
- simpsond 4y agoIt does require more memory for tracking objects as well as storing objects longer than they are strictly necessary. So I would argue that manual memory management is most useful when there are memory constraints. But yes, GCd systems can yield more throughput. They are also easier to work with IMO.
- adgjlsfhk1 4y agothis is only kind of true. garbage collectors can use lots of tricks like alias analysis to automatically free objects without involving the garbage collector, and manually managed languages actually may keep objects around for longer than gced ones because manual management can only use compile time information to determine lifetimes, but GCs get run time info. also, for both types, the trash performance win is avoiding unnecessary allocations in the first place.
- jjtheblunt 4y ago> garbage collectors can use lots of tricks like ... without involving the garbage collector is there a typo: X can use tricks to avoid X ?
- adgjlsfhk1 4y agoMore awkward phrasing due to the line between garbage collectors, compilers and language runtimes being fuzzy. What I mean is that the compiler at compile time decides to act as an alternative garbage collector by allocating and de-allocating the objects in a way that is invisible to the mark and sweep portion.
- Cthulhu_ 4y agoJust because it has a garbage collector doesn't mean it's suboptimal; Go's GC is much faster and less intrusive than that in other languages, and unlike Java, you actually get to choose whether you put things on the heap or stack so you can in theory avoid using the heap entirely, if it makes sense for your application.
- benhoyt 4y ago> you actually get to choose whether you put things on the heap or stack I think I know what you mean, but this isn't strictly true. In Go, the compiler decides for you, and you don't have to worry about it. The compiler allocates on the stack whenever it can (because it's faster and uses less memory), but if it determines that a variable "escapes the stack" or if it can't figure out whether it escapes or not, it allocates on the heap. You could have a simplistic (but correct) Go compiler which allocated everything on the heap -- interestingly, the Go spec doesn't mention the words "heap" or "stack" at all. Go does give you a lot of control over allocations (stack or heap!) and memory layout, though, which makes it pretty good for relatively low-level performance work.
- foobiekr 4y agoIf you are doing real performance coding, you also are avoiding allocations and paying attention to cache sizes and so on. Go is fine for that, hypothetically, though obviously you can do better. But you aren't generating a lot of garbage in the first place so the GC aspect becomes minimal impact. That is, unless you are forced to. One of the things about Go that is _very_ irritating is the way so many key APIs in the ecosystem make it almost impossible to re-use buffers. For example, protobuf and sarama generate throwaway memory at the same rate as message throughput.
- richieartoul 4y agoYeah that’s true, although there is a growing ecosystem of “allocation sensitive” libraries. We use this one a lot for protobuf: https://github.com/richardartoul/molecule https://github.com/richardartoul/molecule jsonparser is great for JSON parsing: https://github.com/buger/jsonparser https://github.com/buger/jsonparser fastcache is a great “zero gc” cache: https://pkg.go.dev/github.com/VictoriaMetrics/fastcache https://pkg.go.dev/github.com/VictoriaMetrics/fastcache Etc
- AlphaSite 4y agoGo’s GC works well because it has stack allocated types, which Java doesn’t and pays a very heavy penalty for.
- astrange 4y ago“Stack allocated” types wouldn’t be a concept at the Java language level, what it’s missing is value types. This has way worse issues than performance, because reference types are unnecessarily mutable and cause lots of bugs. Theoretically it can recover stack allocations with escape analysis though.
- jerryjerryjerry 4y agoInterestingly I have been thinking about related questions too (https://news.ycombinator.com/item?id=31541200#31542407 https://news.ycombinator.com/item?id=31541200#31542407) Language wise, Rust might be a good choice as some projects like tikv already use it in a pretty larger scale systems. But I'd also like to know both pros/cons of these languages.
- jjtheblunt 4y agoI think it's worth noting that Go HAS a garbage collector available, but it's entirely possible to write code which doesn't rely on it, at least in the user-written code. Furthermore, the compiler itself can help in the (re)writing of code to effect stack allocations of variables, avoiding runtime reliance on the garbage collector. This is effected, in general, through "escape analyses" that prevent reliance, at runtime, on garbage collection. Punchline is calling Go a garbage collected language can be entirely inaccurate, depending on the program being compiled.