Manual Memory Management in Go using jemalloc
dgraph.io
dgraph.io
I'm confused about why you need jemalloc instead of just grabbing an N-gigabyte []Node and then manually making them "live" or "dead" by taking pointers. Or take a giant []byte and then cut that up into nodes with unsafe. Seems like those would be equivalent to jemalloc. Maybe throw in some runtime.KeepAlive to keep the GC from scanning it?
Adds in the relevant taxes, such as detecting use after free, etc and that takes a LOT of work
Maybe the arenas reduce the allocations enough (and makes them of reasonably uniform size) such that a simple buddy or slab allocator underneath would beat jemalloc. These simple allocators would have an "unfair" advantage of not having to go through Cgo.
Or maybe just each Allocator (arena) using the Go allocator for its backing allocations would be okay. It'd still be garbage-collected which they've been trying to avoid, but the allocator would no longer looking through each individual allocation, so maybe it'd zippier about freeing stuff.
Note that (as in my other comment) I still think Rust is a better language for this program. In fairness to them, there are plenty of practical reasons they might have ended up here:
* Rust may not have existed or been a reasonable choice when they started coding. In my experience, porting to Rust really is fairly labor-intensive.
* There may be significant other parts of the program where Go/GC is more appropriate.
* Maybe the whole rest of their company's codebase is in Go and they want to use that expertise.
You couldn't be more wrong. Behold the source code to jemalloc and despair.
Allocation can be simple in very specific cases where you can use a single threaded arena.
But it does have that, `runtime.SetFinalizer`. Sure it's clunky, and that's probably intentional: It's just there to be there as the escape hatch for you when you do need to subvert the normal way of doing things.
> Go’s code readability, concurrency model, fast compilation, gofmt, go profiler, go vet, performance and so on. Also, we didn’t ditch Go GC entirely. We’re still using it pretty expansively in all non-critical paths and wherever it’s hard to trace the memory ownership and release.
> I see this as a no different than taking some pieces of your code and converting them to assembly (utilize SIMD instructions, for e.g.), which Go does as well. Also, note that the workload that databases need to incur are lot heavier than typical Go applications.
I don't think most of those are too satisfying as reasons to choose Go over Rust for this program. I think Go's code readability and safety are significantly compromised by this kind of manual memory management, enough so that I'd consider Rust more readable in this context. Likewise Rust's performance is significantly better—the biggest reason Go is faster to compile is that it has a simple compiler that does almost no optimizations. Rust can do that, too, in debug mode, especially with the recent cranelift backend. And Rust has equivalents to "go fmt" and "go vet".
Now it's quite possible that when they started this program years ago, Rust wasn't a good choice. Obviously at least the cranelift backend I just mentioned didn't exist at the time. Pragmatically, you often end up with this kind of "path-dependent design" / "for legacy reasons". Porting is a lot of effort, maybe more than is worth it. You can't always be chasing the hot new fashion or you'd get nothing else done. I think it still should be acknowledged however that a better tool exists now.
I agree with them the concurrency model in Go is genuinely more pleasant than in Rust. In Go, you just write synchronous code and it gets translated to a green thread model for you. In Rust, there's the async stuff which is harder to program and an async ecosystem that is still settling today. That's a clear advantage for Go, and maybe also why they say Go is more readable. (I find Rust otherwise pleasant to read.) And maybe Go is more reasonable for the non-critical path stuff as they said, and maybe that does have to be in the same program.
[1] https://www.reddit.com/r/golang/comments/jobgq9/manual_memor...
They're generalising from the garbage collectors they know to all languages with garbage collectors. Go's GC is particularly crude, and great advances in GC technology have been made in recent years. E.g. take a look at the state of the G1 and ZGC collectors in OpenJDK 15/16.
The benefit of using Go is keeping the memory safety for like 90% of the application, with just a tiny unsafe code portion.
In C, 100% of the source code is unsafe.
On the other hand, if implanting a faster allocator fixes performance problems, then there's something bigger amiss in the overall application design. Creating and destroying small objects at such a high frequency that memory management overhead becomes noticeable isn't a good idea in any case, GC or not.
Is something that not even the Linux kernel with its careful and throughout patch review process is not able to achieve.
The idea that "you can't write safe C" is a big joke. C is as safe as you make it.
https://msrc-blog.microsoft.com/2019/07/16/a-proactive-appro...
https://www.chromium.org/Home/chromium-security/memory-safet...
https://cwe.mitre.org/data/definitions/416.html
https://www.zdnet.com/article/chrome-70-of-all-security-bugs...
Is it perhaps better to focus on context? That is,cost vs benifit wrt context:
- How much safety and what kind and level of safety assurances does the specific application need?
- How much does it cost in development time/friction, application performance, engineering complexity, [insert other relevant cost axes] to achieve the desired level of safety and safety assurances?
Naturally this is a kind of expenses that 99% of the companies aren't going to spend until it finally becomes a legal liability to have security exploits on the software.
https://github.com/golang/go/issues/42430
As usual, Go knows better than other the older languages and then a kludge solution gets done.
Every software has bugs. Whether those bugs are a major blocker for people to use them or not should be taken into account.
The issue that you pointed out are difficult items which need more time and thought to take care of. But they aren't something which should cause any major pain in your day-to-day usage of Go. I would say as of 1.15, GC has improved by leaps and bounds and at this point, all major warts with the system are taken care of.
But then again, I don't know in what context you are using Go. It may be the case that you have been affected by any of these bugs, and unless they get fixed, using Go is a blocker for you.
0 - https://github.com/cretz/go2k/tree/master/compiler/src/test/... 1 - https://github.com/cretz/goahead 2 - https://github.com/golang/go/issues/18602