We are currently running memcached version 1.4.21, but from 1.4.23 the number of max slab classes reduced from 200 to 63, due to a major rewrite of the LRU system.
Recap: memcached uses slab classes to decide where/how to allocate an object. Each slab class is basically a collection of 1MB memory blocks called 'pages', each one divided in 'chunks' of a fixed size. When an item needs to be stored, memcached tries to find the most efficient class to use (i.e. the one with the smallest chunk size to fit the item's size).
The list of slab classes start from a minimum and increases following a growth factor (essentially the minimum size is multiplied by the growth factor n time up to the maximum slab class size, 1MB).
By default, memcached offers two parameters to tune the slab classes:
- -n -> minimum space allocated for key+value+flags (default: 48)
- -f -> chunk size growth factor (default: 1.25)
We have the following configured:
- -n 5
- -f 1.25
This translates into ~180 Slabs, as anybody can see in https://grafana.wikimedia.org/d/000000317/memcache-slabs. The rationale, I believe, for this change at the time was to avoid memory waste with small objects, offering a good granularity of slab classes to allocate objects.
The idea that I have is to do something like T129963, testing one/two shards with a different combination of the above parameters. At the time when I worked on T129963 I had less metrics to check and I had to admit that my knowledge of memcached internals was way less than now (so basically almost zero), and I didn't really analyzed the results as I'd do now. Since we need to reduce the slab classes as part of the upgrade, I'd do some real production tests to see how memcached behaves changing the slab classes distribution.
I created the following Python code to generate the list of slabs:
import argparse import math if __name__ == '__main__': parser = argparse.ArgumentParser(description='Generate slab list for memcached.') parser.add_argument('f', type=float, help='Chunk size growth factor / -f parameter') parser.add_argument('n', type=int, help='Minimum space allocated for key+value+flags') args = parser.parse_args() growth_factor = args.f # sizeof(item) + chunk_size chunk_size = 48 + args.n slab = 1 item_size_max = 1024 * 1024 while True: if chunk_size % 8 != 0: chunk_size += 8 - (chunk_size % 8); print("Slab: {} Chunk: {}".format(str(slab), str(math.floor(chunk_size)))) chunk_size = math.ceil(chunk_size * growth_factor) slab += 1 if chunk_size > (item_size_max / growth_factor): break
Values taken from:
- slabs_init function in slabs.c (memcached 1.4.21)
- memcached.h (the sizeof of the item struct, memcached 1.4.21)