0
|
1 Date: Fri, 5 Sep 2003 20:31:03 +0300
|
|
2 From: Anatoly Vorobey <mellon@pobox.com>
|
|
3 To: memcached@lists.danga.com
|
|
4 Subject: Re: Memory Management...
|
|
5
|
|
6 On Fri, Sep 05, 2003 at 12:07:48PM -0400, Kyle R. Burton wrote:
|
|
7 > prefixing keys with a container identifier). We have just begun to
|
|
8 > look at the implementation of the memory management sub-system with
|
|
9 > regards to it's allocation, de-allocation and compaction approaches.
|
|
10 > Is there any documentation or discussion of how this subsystem
|
|
11 > operates? (slabs.c?)
|
|
12
|
|
13 There's no documentation yet, and it's worth mentioning that this
|
|
14 subsystem is the most active area of memcached under development at the
|
|
15 moment (however, all the changes to it won't modify the way memcached
|
|
16 presents itself towards clients, they're primarily directed at making
|
|
17 memcached use memory more efficiently).
|
|
18
|
|
19 Here's a quick recap of what it does now and what is being worked
|
|
20 on.
|
|
21
|
|
22 The primary goal of the slabs subsystem in memcached was to eliminate
|
|
23 memory fragmentation issues totally by using fixed-size memory chunks
|
|
24 coming from a few predetermined size classes (early versions of
|
|
25 memcached relied on malloc()'s handling of fragmentation which proved
|
|
26 woefully inadequate for our purposes). For instance, suppose
|
|
27 we decide at the outset that the list of possible sizes is: 64 bytes,
|
|
28 128 bytes, 256 bytes, etc. - doubling all the way up to 1Mb. For each
|
|
29 size class in this list (each possible size) we maintain a list of free
|
|
30 chunks of this size. Whenever a request comes for a particular size,
|
|
31 it is rounded up to the closest size class and a free chunk is taken
|
|
32 from that size class. In the above example, if you request from the
|
|
33 slabs subsystem 100 bytes of memory, you'll actually get a chunk 128
|
|
34 bytes worth, from the 128-bytes size class. If there are no free chunks
|
|
35 of the needed size at the moment, there are two ways to get one: 1) free
|
|
36 an existing chunk in the same size class, using LRU queues to free the
|
|
37 least needed objects; 2) get more memory from the system, which we
|
|
38 currently always do in _slabs_ of 1Mb each; we malloc() a slab, divide
|
|
39 it to chunks of the needed size, and use them.
|
|
40
|
|
41 The tradeoff is between memory fragmentation and memory utilisation. In
|
|
42 the scheme we're now using, we have zero fragmentation, but a relatively
|
|
43 high percentage of memory is wasted. The most efficient way to reduce
|
|
44 the waste is to use a list of size classes that closely matches (if
|
|
45 that's at all possible) common sizes of objects that the clients
|
|
46 of this particular installation of memcached are likely to store.
|
|
47 For example, if your installation is going to store hundreds of thousands of objects of the size exactly 120 bytes, you'd be much better
|
|
48 off changing, in the "naive" list of sizes outlined above, the class
|
|
49 of 128 bytes to something a bit higher (because the overhead of
|
|
50 storing an item, while not large, will push those 120-bytes objects over
|
|
51 128 bytes of storage internally, and will require using 256 bytes for
|
|
52 each of them in the naive scheme, forcing you to waste almost 50% of
|
|
53 memory). Such tinkering with the list of size classes is not currently
|
|
54 possible with memcached, but enabling it is one of the immediate goals.
|
|
55
|
|
56 Ideally, the slabs subsystem would analyze at runtime the common sizes
|
|
57 of objects that are being requested, and would be able to modify the
|
|
58 list of sizes dynamically to improve memory utilisation. This is not
|
|
59 planned for the immediate future, however. What is planned is the
|
|
60 ability to reassign slabs to different classes. Here's what this means.
|
|
61 Currently, the total amount of memory allocated for each size class is
|
|
62 determined by how clients interact with memcached during the initial
|
|
63 phase of its execution, when it keeps malloc()'ing more slabs and
|
|
64 dividing them into chunks, until it hits the specified memory limit
|
|
65 (say, 2Gb, or whatever else was specified). Once it hits the limit, to
|
|
66 allocate a new chunk it'll always delete an existing chunk of the same
|
|
67 size (using LRU queues), and will never malloc() or free() any memory
|
|
68 from/to the system. So if, for example, during those initial few hours
|
|
69 of memcached's execution your clients mainly wanted to store very small
|
|
70 items, the bulk of memory allocated will be divided to small-sized
|
|
71 chunks, and the large size classes will get fewer memory, therefore the
|
|
72 life-cycle of large objects you'll store in memcached will henceforth
|
|
73 always be much shorter, with this instance of memcached (their LRU
|
|
74 queues will be shorter and they'll be pushed out much more often). In
|
|
75 general, if your system starts producing a different pattern of common
|
|
76 object sizes, the memcached servers will become less efficient, unless
|
|
77 you restart them. Slabs reassignment, which is the next feature being
|
|
78 worked on, will ensure the server's ability to reclaim a slab (1Mb of
|
|
79 memory) from one size class and put it into another class size, where
|
|
80 it's needed more.
|
|
81
|
|
82 --
|
|
83 avva
|