Live data from Hacker News

The effect of switching to TCMalloc on RocksDB memory use

blog.cloudflare.com

31–40 of 49 posts

Re: The effect of switching to TCMalloc on RocksDB memory use

#31

The blog post sadly doesn't cover what alternatives are out there, and why they settled on their choice. Besides tcmalloc, there are jmalloc, hoard, the more recent mimalloc, a bunch of commercial options, ... Would have been interesting to see a comparison. Edit: background is in this thread: https://news.ycombinator.com/item?id=26013230

The best allocator for this kind of problem would of course be a compacting GC. First it would be much faster, it would be safer, and easier to use.

For my bigger libraries glibc malloc and free has insane runtimes. Eg a final free could last over 1 minute. If you see this with a GC you would throw it out immediately. But apparently people don't measure and spread false rumors about GC's. Even a very poor and primitive GC as in emacs is not that slow as glibc malloc/free. A good one is miles faster. My GC has an upper limit of 10ms, not minutes.

Re: The effect of switching to TCMalloc on RocksDB memory use

#32

I wrote a commercial memory allocator (long long gone) for pre MacOS 10 and tested it by writing a ton of memory bashing applications based on every perverse pattern I could think of in order to ensure it was fast, failure free and limited fragmentation. Of course from that era I didn't need to support multiple threads so that made it somewhat easier, but in building it (and studying others at the time) it was obviou…

> Computers may be ridiculously fast and have enormous memory today and maybe you can live with the default Often these hardware improvements exacerbate problems with memory management. For years the runtime of the default JVM garbage collector would blow up past 48 GB of allocations. Which was probably fine for 99% of software out there, but the day your honking great server blew past that... was a bad day all round…

I'm not quite sure why, but Java/JVM seems to be uniquely bad with regards to memory usage. Nothing else seems to use quite so much memory. It's expected that C/C++/Rust would be better, but similar languages like C# also seems much better. Even Python/Ruby/JavaScript don't seem as bad.

Re: The effect of switching to TCMalloc on RocksDB memory use

#33
The author notes that they could have decreased MALLOC_ARENA_MAX but it would have decreased throughput. But how much does switching to TCMalloc affect the throughput of the application compared to decreasing MALLOC_ARENA_MAX? (And if the answer is that it's insignificant, why isn't it the default?)

Re: The effect of switching to TCMalloc on RocksDB memory use

#34

I've spent some time really studying memory allocation. It seems to me that almost any high-performance application will take advantage of the fine-details of memory allocators to optimize themselves. There's something to be said about garbage collection as an actual strategy (!!). I know people think that garbage collection is slow, but... very smart people have put their minds on the memory problem. A generational…

I read such a statement from Andras Kovacs re: compilers https://www.reddit.com/r/haskell/comments/gok70o/simple_hask...

One needs different allocation strategies for immutable (and strict/lazy) languages.

Re: The effect of switching to TCMalloc on RocksDB memory use

#35
post #2

This is really a thing which happens with the glibc allocator and long-lived applications. Back in the day I had a custom chat server which consistently used more and more memory until after about a month it ran out of memory and crashed. I spent quite a while investigating, even annotating every malloc/free in the program to find the leak, only to conclude that there was simply no leak at all in the program and it w…

for long-running daemon/server programs where reliability is a key priority, I have had a lot of success building in "partial" restarts into the design, which means that periodically the program exits the main event loop and enters it again.

clearly there are circumstances where this is not an option, but for distributed systems that can handle the short-term loss of a given node it works very well. one important thing is to add is some randomized jitter to the periodic restarts to spread out when nodes are offline.

in practice this remedies a wide range of seldom-encountered problems. for instance, I have a program that pulls data from an http apis, and last week its connection to one of the remote servers became hung up somehow, resulting in no new data being pulled from that server. this problem had never happened before in 6 months since the program was deployed. rather than track down the very rare condition that was behind this I just added periodic "partial" restarts to the program, and now it will be able to recover from this if it ever happened again, and many other sorts of problems like slowing increasing memory fragmentation.

for a chat server or other server with client connections, obviously those would need to be maintained across the partial restart, but that seems like it would be pretty easy to do. while there is nothing dramatically different from my approach to what you did with actual, full periodic restarts, the partial restart approach does allow special handling for application-specific issues like that.

Re: The effect of switching to TCMalloc on RocksDB memory use

#36
post #33

The author notes that they could have decreased MALLOC_ARENA_MAX but it would have decreased throughput. But how much does switching to TCMalloc affect the throughput of the application compared to decreasing MALLOC_ARENA_MAX? (And if the answer is that it's insignificant, why isn't it the default?)

The main problem with this approach for us is not throughput, MALLOC_ARENA_MAX is a maximum number of arenas per core, and our new servers have 48 cores, meaning we would still have a huge number of arenas and fragmentation problem. Yes, it will be a bit better but not even close to tcmalloc. There is a good post by Heroku with some tests of different MALLOC_ARENA_MAX, that can give more insight on performance hit. https://devcenter.heroku.com/articles/testing-cedar-14-memor...

Re: The effect of switching to TCMalloc on RocksDB memory use

#37
post #30

I've spent some time really studying memory allocation. It seems to me that almost any high-performance application will take advantage of the fine-details of memory allocators to optimize themselves. There's something to be said about garbage collection as an actual strategy (!!). I know people think that garbage collection is slow, but... very smart people have put their minds on the memory problem. A generational…

This sort of thing is why I'm generally not impressed by the perennial "GC vs. manual memory management" debate. Manual memory management usually isn't. Simply using "malloc" isn't manual memory management, there's still a lot of stuff going on behind the scenes that may or may not match your program's use case. Both GC and "manual" memory management aren't single points, they're suites of options, which you can ofte…

> "Pie tastes better than cake!"

Indeed. And is Cheesecake a pie or a cake? You bake cheesecake in pie-crust, but it has cake in the name.

The memory-management "its both" scheme is reference-counting. In some contexts, its ref-counts are considered manual (C++ shared_ptr, Rust). In other contexts, its considered automated garbage collection (Python, Lisp).

In all cases: tons of atomic add/subtract + memory-barriers to strictly order reference counts between many threads... making it actually kind of bad for multithreaded code compared to other garbage collection schemes. (Each shared_ptr = blah() requires an atomic add and atomic-subtract. So you actually have escalating costs the more you use the shared_ptr). I think the circular-issue is kind of overblown: it certainly happens (doubly-linked lists, graphs, trees which store a "root pointer" or even an "iterator" somewhere inside of them) but people know how to use weak_ptr these days and avoid most issues. (Well... except for arbitrary graphs. No way to know where to put weak_ptr there. But most "graph heavy" algorithms use other representations: dense matrix or sparse matrix forms... and probably should avoid slow / low-performance general purpose memory allocators anyway)

-----------

Once you recognize the multithreaded-sync issues associated with shared_ptr / RefCount, you start building a thread that "centralizes" the ref-counts in a reader/writer queue to ensure that objects are deleted at the right time. Oh wait, that's called a garbage collector thread and you've suddenly moved to mark&sweep accidentally.

Once you get a garbage collector thread, its not too big of a leap to start thinking about multiple garbage-collector threads (scaling to higher garbage collection performance). I mean, yeah, I'm glossing over all sorts of high-performance multithreaded lock-free datastructures here, but lets pretend those implementation details are solved. Lol.

-----------

As usual: its not really the garbage collection or memory-management parts that are hard. Its the multithreaded high-performance (and provably correct!) portion that's really, really hard. Even for simple ideas like ref-counts or even "manual" malloc/free.

Re: The effect of switching to TCMalloc on RocksDB memory use

#38

I wrote a commercial memory allocator (long long gone) for pre MacOS 10 and tested it by writing a ton of memory bashing applications based on every perverse pattern I could think of in order to ensure it was fast, failure free and limited fragmentation. Of course from that era I didn't need to support multiple threads so that made it somewhat easier, but in building it (and studying others at the time) it was obviou…

Is heap management that far from the algorithmic headaches of Garbage Collection? Compiled languages typically are "arena allocators" by default and probably don't hit these similar heap management/paring/centralizing/compaction problems until they do a ton of dynamic datastructure allocations and manipulations.

... and they get to punt to the OS too.

I'd certainly believe this isn't a "solved" problem.

Re: The effect of switching to TCMalloc on RocksDB memory use

#39

I wrote a commercial memory allocator (long long gone) for pre MacOS 10 and tested it by writing a ton of memory bashing applications based on every perverse pattern I could think of in order to ensure it was fast, failure free and limited fragmentation. Of course from that era I didn't need to support multiple threads so that made it somewhat easier, but in building it (and studying others at the time) it was obviou…

> Computers may be ridiculously fast and have enormous memory today and maybe you can live with the default Often these hardware improvements exacerbate problems with memory management. For years the runtime of the default JVM garbage collector would blow up past 48 GB of allocations. Which was probably fine for 99% of software out there, but the day your honking great server blew past that... was a bad day all round…

Yeah, it's a huge probelm with Cassandra.

The 4.0 version of cassandra is finally doing the smart thing and spinning up separate threads dedicated to sets/shards of hash ranges held by a node. I'm not sure if they'll run different JVMs per thread as well to do further sharding of the GC generations as well or if they can effectively do that from one JVM, but that would make sense

Re: The effect of switching to TCMalloc on RocksDB memory use

#40

I've spent some time really studying memory allocation. It seems to me that almost any high-performance application will take advantage of the fine-details of memory allocators to optimize themselves. There's something to be said about garbage collection as an actual strategy (!!). I know people think that garbage collection is slow, but... very smart people have put their minds on the memory problem. A generational…

Rust is probably close to a good idea: have the programmer give directions as to the lifetime/behavior of the data and allocated memory, rather than having to guess.

Compilers might be able to do some of that, but at least the standard libraries should be able to do the hints, and almost all dynamic allocations on day-to-day code uses dynamic structures in standard libraries like hashmaps and variable-length lists/vectors/arrays.

Post reply on HN