Jason Evans wrote: > On 28 November, I announced a new malloc implementation that I've been > working on as a replacement for the current libc malloc. The main goal > for this rewrite of malloc is to provide better scalability for > multi-threaded programs on SMP systems, without degrading performance > in general. The benchmarks I've run indicate that the code now meets > this goal. > > === > === Resolved issues === > === > > Several people provided valuable feedback regarding architecture, (in) > correct function, and performance. At this point, I think I have > addressed all outstanding issues. Here's a recap: > > * Hiten Pandya and others requested that I used the sys/tree.h > implementation of red-black trees rather than my own. This is done. > > * Jon Dama objected to leaving sbrk() support out of the malloc > implementation for 32-bit architectures. I've added support for sbrk () > on i386 and arm. > > * David Xu pointed out that the hand-rolled spinlocks did not do > priority inheritance, which could lead to priority inversion > deadlocks. This has been fixed by using the libc spinlocks. > > * Kris Kennaway discovered that sparc64 doesn't have TLS, and Olivier > Houchard contacted me about the same issue for arm. I removed the use > of TLS on those architectures. > > * Claus Guttesen and Devon O'Dell reported issues in kldxref and X on > amd64. The kldxref problem has been fixed. The X problem is a bit > trickier. I borrowed a Turion-based laptop from Rob Braun and indeed > found that X wouldn't start. Eric Anholt suggested trying the xorg- > server-snap port rather than xorg-server, and the problem went away. > Eric explained that xorg-server uses custom ELF *.a module loading (the > idea being that the same exact module could be used on different > operating systems), whereas xorg-server-snap uses dlopen(3) and > friends. Apparently, we are on the cusp of the transition to using the > latter, so for now the workaround on amd64 is to use xorg-server- snap, > with the expectation that soon the standard xorg-server port will be > upgraded. > > === > === Benchmarking ad nauseam === > === > > ==> Lever & Boreham benchmarks (multi-threaded) > > Chuck Lever and David Boreham published "malloc() Performance in a > Multithreaded Linux Environment" in the proceedings of the FREENIX > track of the 2000 USENIX Annual Technical Conference. By some > incredible stroke of luck, I found the source code for their > benchmarks, and Kris Kennaway ran the first of them on a 14-CPU sparc64 > system as well as a dual-dual opteron system. (The other two > benchmarks aren't very enlightening.) It's important to keep in mind > that this is a micro-benchmark that demonstrates a worst case scenario > for non-SMP-scalable malloc implementations. That said, with 5 > threads, jemalloc outperformed phkmalloc by 15X on sparc64 and 80X on > amd64. > > ==> super-smack (multi-threaded) > > The SMP scalability tests were very encouraging, but Kris also ran some > super-smack benchmarks on a dual-dual opteron system, and jemalloc was > actually a bit slower than phkmalloc in that case (~2.7-3.7% slower, > depending on jemalloc configuration). I've been obsessing over that > for over a week now, and one of the results is that jemalloc is now > more tunable -- delayed coalescence cache size, number of allocation > arenas, and allocation quantum size are all tunable via MALLOC_OPTIONS > now. > > However, changing the tuning parameters wasn't enough to make up the > performance difference for the super-smack benchmarks. So, I spent > some time looking at memory fragmentation. Taking a cue from Poul- > Henning Kamp's malloc work, I finally wrote a program that converts > ktrace output to memory usage graphs. Here's a series of of graphs > from various stages of the fragmentation avoidance changes I made. The > test program was 'kldxref /boot/kernel' on amd64. > > http://people.freebsd.org/~jasone/jemalloc/plots/kldxref.png > http://people.freebsd.org/~jasone/jemalloc/plots/kldxref13.gif > http://people.freebsd.org/~jasone/jemalloc/plots/kldxref15.gif > > The interesting thing to note is that in the first two plots, the > highly fragmented portion of the graph continuously grows, wheareas in > the last plot, the fragmented portion stops growing about 1/3 of the > way through the program run. (The first plot only shows the bottom > half of what the other two plots show.) This is all really cool, but > alas, it didn't help the super-smack results. I was able to use the > graphing program to determine that cache line sharing was potentially > causing even more issues than suspected, but we already knew that cache > line sharing was an issue as a result of some of the runs that Kris did. > > Finally, last night I discovered that overzealous use of __inline is > likely to have been the culprit. I removed the __inline keyword from > all of the functions that aren't part of the critical path for small > memory allocation/deallocation, and measured a substantial performance > improvement. > > I don't have access to the machine that Kris ran the super-smack > benchmarks on, so I set up a couple of VMware guest systems using > VMware 5.5 on a 3.2 GHz P4 system (HTT enabled for the host, but not > the guests). The two guests were configured very similarly, so that > the only pertinent difference was that one was using phkmalloc, and the > other was using jemalloc. Before the __inline changes, jemalloc was > ~2.5% slower than phkmalloc. Here are the final results, which show > jemalloc to be ~4.1% faster than phkmalloc for the benchmark: > > x ss_phk > + ss_je > +----------------------------------------------------------------------- > ---+ > |xxx x x x x x x + x + ++ ++ + + > + +| > | |_____________A__M__________| | > ___________A___M______| | > +----------------------------------------------------------------------- > ---+ > N Min Max Median Avg > Stddev > x 10 1594.94 1658.64 1623.73 1619.431 > 21.833342 > + 10 1649.06 1706.75 1693.23 1686.275 > 17.410025 > Difference at 95.0% confidence > 66.844 +/- 18.5532 > 4.12762% +/- 1.14566% > (Student's t, pooled s = 19.7459) > > The numbers reported are queries/second, so bigger is better. > > I used mysql 5.0.16, super-smack 1.3, libthr, and MALLOC_OPTIONS='aj', > with 10 replicates of the following command for each VMware guest: > > super-smack -d mysql select-key.smack 4 10000 > > It's worth noting that, as near as I can tell, almost all memory > allocation happens in a single mysqld thread. Presumably, a master > thread is handing buffers to slaves, which process the buffers, then > free them. As such, this test doesn't benefit from jemalloc's multiple > arenas, so it is not a very good test of SMP scalability, at least with > regard to malloc. > > ==> cfrac (single-threaded) > > cfrac is one of many benchmark programs that is included in the Hoard > memory allocator source distribution (see http://www.cs.umass.edu/ > ~emery/hoard/). I ran cfrac as follows: > > cfrac 47582602774358115722167492755475367767 > > (Same VMware setup as for super-smack benchmarks.) > > Wall time (seconds) > phkmalloc 'aj': 18.75, 18.68, 18.69 > jemalloc 'aj': 17.57, 17.65, 17.56 > > I haven't looked at allocation traces of cfrac in quite a while, but > IIRC, it does a lot of small allocations of various sizes. > > ==> sh6bench (single-threaded) > > sh6bench (see http://www.microquill.com/smartheap/shbench/bench.zip) is > a quirky malloc benchmark that has been used in some of the published > malloc literature, so I include it here. sh6bench does cycles of > allocating groups of objects, where the size of objects in each group > is random. Sometimes the objects are held for a while before being > freed. I ran sh6bench with the following interactive runtime parameters: > > call count: 50000 > min block size: 1 > max block size: 1000 > > phkmalloc doesn't fare very well with its default settings since the > benchmark's memory usage fluctuates enough to cause phkmalloc to > repeatedly allocate and free pages. Therefore I report numbers for > multiple MALLOC_OPTIONS settings. Since the program prompts for > interactive input, I report the sum of user and sys time, rather than > wall time. (Same VMware setup as for super-smack benchmarks.) > > user+sys time (seconds) > phkmalloc 'aj': 25.66, 25.78, 22.50 > phkmalloc 'aj>>>>': 13.72, 13.77, 13.69 > jemalloc 'aj': 17.88, 17.05, 17.10 > > If phkmalloc's cache size is increased adequately, it beats jemalloc. > jemalloc simply has to do more work when splitting and coalescing > regions than phkmalloc does, and this benchmark severely stresses that > aspect of jemalloc. It's perhaps worth noting that jemalloc's peak > memory usage is ~22% lower than phkmalloc's, which means that it's > doing a better job of avoiding fragmentation. At least the extra > algorithmic overhead gains us something. > > ==> gs (single-threaded) > > Ghostscript is the well known PostScript interpreter. I took the > biggest PDF I have (the PostScript Lanuguage Reference, 3rd Ed.) and > converted it to PostScript, then ran AFPL Ghostscript 8.53 as follows: > > gs -dBATCH -dNODISPLAY PS3.ps > > As with sh6bench, memory usage fluctuated enough to cause excessive > allocation/deallocation of pages with phkmalloc, so I report times for > multiple MALLOC_OPTIONS settings. (Same VMware setup as for > super-smack benchmarks.) > > Wall time (seconds) > phkmalloc 'aj': 92.12, 92.03, 92.90 > phkmalloc 'aj>>>>': 61.11, 61.29, 61.73 > jemalloc 'aj': 62.34, 62.43, 62.20 > jemalloc 'ajQQQc': 60.85, 60.48, 60.81 > > Okay, the 'ajQQQc' parameterization for jemalloc is a bit silly, but if > phkmalloc gets help, then so should jemalloc. =) > > === > === Proposed approach for inclusion in CURRENT === > === > > Here's a current version of all the changes that are necessary for > jemalloc to go in to the tree: > > http://people.freebsd.org/~jasone/jemalloc/patches/ > jemalloc_20051222c.diff > > This patch includes (roughly): > > 1) Fix gensnmptree bug (missing variable initialization). I emailed > the maintainer, Hartmut Brandt, about this today, and am awaiting a reply. > > 2) Fix kldxref bug (assumes allocation is adequately aligned). This > needs to be committed along with (5), since the fix uses posix_memalign(). > > 3) Move calloc() from calloc.c to malloc.c (enables better statistics > gathering). > > 4) Add _malloc_{pre,post}fork(), for use by threading libraries. > > 5) Replace malloc(), calloc(), realloc(), and free(). Add > posix_memalign(). > > I'd like to commit (3) and (4) first, so that we have a version of > phkmalloc in the cvs repository that is API-compatible with libc as it > will be after jemalloc is committed. This will make it easier to swap > the phkmalloc code in, should we wish to do further benchmarking or > regression testing at some point in the future. Poul-Henning has > agreed with this in principle, though I haven't yet supplied him with > diffs for only (3) and (4). > > So, how about it? Is jemalloc ready to go in now? > > Thanks, > Jason I think that this is overall a good plan. Two things need to be stressed, since they will quickly become FAQs. First is that phkmalloc and jemalloc won't be interchangable at runtime, like the threading libraries are. Second is that amd64 will be stuck without working X until the 6.9/7.0 stuff gets in the tree, and people should be well aware of this. Another thing that I worry about is complex library scenarios where you might have different versions of libc getting pulled into the same process by different library dependencies. This could turn into a big headache that is only solvable by telling people to wipe out their entire ports collection and rebuild from scratch. Does this warrant a library version bump now (as much as I really don't want to advocate this)? ScottReceived on Fri Dec 23 2005 - 08:31:21 UTC
This archive was generated by hypermail 2.4.0 : Wed May 19 2021 - 11:38:49 UTC