Discussion on redis using different memory allocators tcmalloc and jemalloc

  • 2020-05-15 02:30:10
  • OfStack

We know that Redis does not implement memory pools on its own, adding its own stuff to the standard system memory allocator. Therefore, the performance and fragmentation rate of the system memory allocator will have some performance impact on Redis.

In Redis's zmalloc.c source code, we can see the following code:


/* Double expansion needed for stringification of macro values. */ 
#define __xstr(s) __str(s) 
#define __str(s) #s 
#if defined(USE_TCMALLOC) 
#define ZMALLOC_LIB ("tcmalloc-" __xstr(TC_VERSION_MAJOR) "." __xstr(TC_VERSION_MINOR)) 
#include <google/tcmalloc.h> 
#if (TC_VERSION_MAJOR == 1 && TC_VERSION_MINOR >= 6) || (TC_VERSION_MAJOR > 1) 
#define HAVE_MALLOC_SIZE 1 
#define zmalloc_size(p) tc_malloc_size(p) 
#else 
#error "Newer version of tcmalloc required" 
#endif 
#elif defined(USE_JEMALLOC) 
#define ZMALLOC_LIB ("jemalloc-" __xstr(JEMALLOC_VERSION_MAJOR) "." __xstr(JEMALLOC_VERSION_MINOR) "." __xstr(JEMALLOC_VERSION_BUGFIX)) 
#include <jemalloc/jemalloc.h> 
#if (JEMALLOC_VERSION_MAJOR == 2 && JEMALLOC_VERSION_MINOR >= 1) || (JEMALLOC_VERSION_MAJOR > 2) 
#define HAVE_MALLOC_SIZE 1 
#define zmalloc_size(p) je_malloc_usable_size(p) 
#else 
#error "Newer version of jemalloc required" 
#endif 
#elif defined(__APPLE__) 
#include <malloc/malloc.h> 
#define HAVE_MALLOC_SIZE 1 
#define zmalloc_size(p) malloc_size(p) 
#endif 
#ifndef ZMALLOC_LIB 
#define ZMALLOC_LIB "libc" 
#endif 

As you can see from the code above, Redis determines whether to use tcmalloc at compile time, and if so, replaces the standard libc implementation with its tcmalloc counterpart. The second is to determine if jemalloc is enabled, and the last is to use the standard memory management functions in libc if none is used.

In the latest version 2.4.4, jemalloc is included in the source package as part 1, so it can be used directly. If you want to use tcmalloc, you need to install it yourself.

Here's how to install the tcmalloc package. tcmalloc is part of google-proftools, so we actually need to install google-proftools. If you are installing on a 64-bit machine, you will need to install the libunwind library on which it relies.

wget http://download.savannah.gnu.org/releases/libunwind/libunwind-0.99-alpha.tar.gz tar zxvf libunwind-0.99-alpha.tar.gz cd libunwind-0.99-alpha/ CFLAGS=-fPIC ./configure make CFLAGS=-fPIC make CFLAGS=-fPIC install

Then the installation of google-preftools was carried out:

wget http://google-perftools.googlecode.com/files/google-perftools-1.8.1.tar.gz tar zxvf google-perftools-1.8.1.tar.gz cd google-perftools-1.8.1/ ./configure --disable-cpu-profiler --disable-heap-profiler --disable-heap-checker --disable-debugalloc --enable-minimal make && make install sudo echo "/usr/local/lib" > / etc/ld so. conf. d/usr_local_lib conf # if there is no this file, build a sudo/sbin/ldconfig

Then you install Redis and specify the appropriate parameters to enable tcmalloc at make

$ curl -O http://redis.googlecode.com/files/redis-2.4.4.tar.gz $ tar xzvf redis-2.4.4.tar.gz $ cd redis-2.4.4 $ make USE_TCMALLOC=yes FORCE_LIBC_MALLOC=yes $ sudo make install

After starting Redis again, you can see the memory allocator in use through the info command.

To return to the topic of this article, there are three memory allocators for tcmalloc, jemalloc, and libc. What about performance and fragmentation rates?

Here is a simple test result, using Redis's own redis-benchmark to write the same amount of data from Redis info information using different allocator.

As can be seen, the fragment rate of tcmalloc is the lowest, which is 1.01, jemalloc is 1.02, and libc's allocator fragment rate is 1.31, which is not as follows:

used_memory:708391440 used_menory_human:675.57M used_memory_rss:715169792 used_memory_peak:708814040 used_memory_peak_human:675.98M mem_fragmentation_ratio:1.01mem_allocator:tcmalloc-1.7

used_memory:708381168 used_menory_human:675.56M used_memory_rss:723587072 used_memory_peak:708803768 used_memory_peak_human:675.97M mem_fragmentation_ratio:1.02mem_allocator:jemalloc-2.2.1

used_memory:869000400 used_menory_human:828.74M used_memory_rss:1136689152 used_memory_peak:868992208 used_memory_peak_human:828.74M mem_fragmentation_ratio:1.31mem_allocator:libc

The above test data are all small data, that is to say, the single data is not large. Next, we try to set the -d parameter of benchmark and adjust the value of value to the size of 1k, and the test result has some changes:

used_memory:830573680 used_memory_human:792.10M used_memory_rss:849068032 used_memory_peak:831436048 used_memory_peak_human:792.92M mem_fragmentation_ratio:1.02mem_allocator:tcmalloc-1.7

used_memory:915911024 used_memory_human:873.48M used_memory_rss:927047680 used_memory_peak:916773392 used_memory_peak_human:874.30M mem_fragmentation_ratio:1.01mem_allocator:jemalloc-2.2.1

used_memory:771963304 used_memory_human:736.20M used_memory_rss:800583680 used_memory_peak:772784056 used_memory_peak_human:736.98M mem_fragmentation_ratio:1.04mem_allocator:libc

It can be seen that the fragmentation rate difference between several allocators is relatively large in allocating large chunks of memory and small chunks of memory. When you use Redis, you should try your best to test with your real data so as to select the allocators that are most suitable for your data.


Related articles: