Skip to content

Commit

Permalink
Enable use of large memory pages (Trond Norbye) <[email protected]>
Browse files Browse the repository at this point in the history
Initial support for solaris.


git-svn-id: http://code.sixapart.com/svn/memcached/trunk/server@724 b0b603af-a30f-0410-a34e-baf09ae79d0b
  • Loading branch information
Trond Norbye authored and Trond Norbye committed Feb 25, 2008
1 parent c12ebb2 commit a6b35b4
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 8 deletions.
2 changes: 2 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,8 @@ if test "x$enable_threads" == "xyes"; then
fi

AC_CHECK_FUNCS(mlockall)
AC_CHECK_FUNCS(getpagesizes)
AC_CHECK_FUNCS(memcntl)

AC_CONFIG_FILES(Makefile doc/Makefile)
AC_OUTPUT
14 changes: 13 additions & 1 deletion doc/memory_management.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
Date: Tue, 20 Feb 2008
From: Trond Norbye <[email protected]>

When started with -L memcached will try to enable large memory
pages, and preallocate all memory up front. By using large memory
pages memcached could reduce the number of TLB misses (depending
on the access pattern), and hence improve performance.

See http://en.wikipedia.org/wiki/Translation_lookaside_buffer for
a description of TLB.

Date: Fri, 5 Sep 2003 20:31:03 +0300
From: Anatoly Vorobey <[email protected]>
To: [email protected]
Expand Down Expand Up @@ -44,7 +55,8 @@ high percentage of memory is wasted. The most efficient way to reduce
the waste is to use a list of size classes that closely matches (if
that's at all possible) common sizes of objects that the clients
of this particular installation of memcached are likely to store.
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
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
off changing, in the "naive" list of sizes outlined above, the class
of 128 bytes to something a bit higher (because the overhead of
storing an item, while not large, will push those 120-bytes objects over
Expand Down
65 changes: 62 additions & 3 deletions memcached.c
Original file line number Diff line number Diff line change
Expand Up @@ -2693,7 +2693,17 @@ static void usage(void) {
"-b run a managed instanced (mnemonic: buckets)\n"
"-P <file> save PID in <file>, only used with -d option\n"
"-f <factor> chunk size growth factor, default 1.25\n"
"-n <bytes> minimum space allocated for key+value+flags, default 48\n");
"-n <bytes> minimum space allocated for key+value+flags, default 48\n"

#if defined(HAVE_GETPAGESIZES) && defined(HAVE_MEMCNTL)
"-L Try to use large memory pages (if available). Increasing\n"
" the memory page size could reduce the number of TLB misses\n"
" and improve the performance. In order to get large pages\n"
" from the OS, memcached will allocate the total item-cache\n"
" in one large chunk.\n"
#endif
);

#ifdef USE_THREADS
printf("-t <num> number of threads to use, default 4\n");
#endif
Expand Down Expand Up @@ -2804,11 +2814,53 @@ static void sig_handler(const int sig) {
exit(EXIT_SUCCESS);
}

#if defined(HAVE_GETPAGESIZES) && defined(HAVE_MEMCNTL)
/*
* On systems that supports multiple page sizes we may reduce the
* number of TLB-misses by using the biggest available page size
*/
int enable_large_pages(void) {
int ret = -1;
size_t sizes[32];
int avail = getpagesizes(sizes, 32);
if (avail != -1) {
size_t max = sizes[0];
struct memcntl_mha arg = {0};
int ii;

for (ii = 1; ii < avail; ++ii) {
if (max < sizes[ii]) {
max = sizes[ii];
}
}

arg.mha_flags = 0;
arg.mha_pagesize = max;
arg.mha_cmd = MHA_MAPSIZE_BSSBRK;

if (memcntl(0, 0, MC_HAT_ADVISE, (caddr_t)&arg, 0, 0) == -1) {
fprintf(stderr, "Failed to set large pages: %s\n",
strerror(errno));
fprintf(stderr, "Will use default page size\n");
} else {
ret = 0;
}
} else {
fprintf(stderr, "Failed to get supported pagesizes: %s\n",
strerror(errno));
fprintf(stderr, "Will use default page size\n");
}

return ret;
}
#endif

int main (int argc, char **argv) {
int c;
int x;
bool lock_memory = false;
bool daemonize = false;
bool preallocate = false;
int maxcore = 0;
char *username = NULL;
char *pid_file = NULL;
Expand All @@ -2833,7 +2885,7 @@ int main (int argc, char **argv) {
setbuf(stderr, NULL);

/* process arguments */
while ((c = getopt(argc, argv, "a:bp:s:U:m:Mc:khirvdl:u:P:f:s:n:t:D:")) != -1) {
while ((c = getopt(argc, argv, "a:bp:s:U:m:Mc:khirvdl:u:P:f:s:n:t:D:L")) != -1) {
switch (c) {
case 'a':
/* access for unix domain socket, as octal mask (like chmod)*/
Expand Down Expand Up @@ -2917,6 +2969,13 @@ int main (int argc, char **argv) {
settings.prefix_delimiter = optarg[0];
settings.detail_enabled = 1;
break;
#if defined(HAVE_GETPAGESIZES) && defined(HAVE_MEMCNTL)
case 'L' :
if (enable_large_pages() == 0) {
preallocate = true;
}
break;
#endif
default:
fprintf(stderr, "Illegal argument \"%c\"\n", c);
return 1;
Expand Down Expand Up @@ -3056,7 +3115,7 @@ int main (int argc, char **argv) {
conn_init();
/* Hacky suffix buffers. */
suffix_init();
slabs_init(settings.maxbytes, settings.factor);
slabs_init(settings.maxbytes, settings.factor, preallocate);

/* managed instance? alloc and zero a bucket array */
if (settings.managed) {
Expand Down
51 changes: 49 additions & 2 deletions slabs.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,15 @@ static size_t mem_limit = 0;
static size_t mem_malloced = 0;
static int power_largest;

static void *mem_base = NULL;
static void *mem_current = NULL;
static size_t mem_avail = 0;

/*
* Forward Declarations
*/
static int do_slabs_newslab(const unsigned int id);
static void *memory_allocate(size_t size);

#ifndef DONT_PREALLOC_SLABS
/* Preallocate as many slab pages as possible (called from slabs_init)
Expand Down Expand Up @@ -92,7 +97,7 @@ unsigned int slabs_clsid(const size_t size) {
* Determines the chunk sizes and initializes the slab class descriptors
* accordingly.
*/
void slabs_init(const size_t limit, const double factor) {
void slabs_init(const size_t limit, const double factor, const bool prealloc) {
int i = POWER_SMALLEST - 1;
unsigned int size = sizeof(item) + settings.chunk_size;

Expand All @@ -101,6 +106,19 @@ void slabs_init(const size_t limit, const double factor) {
size = 128;

mem_limit = limit;

if (prealloc) {
/* Allocate everything in a big chunk with malloc */
mem_base = malloc(mem_limit);
if (mem_base != NULL) {
mem_current = mem_base;
mem_avail = mem_limit;
} else {
fprintf(stderr, "Warning: Failed to allocate requested memory in"
" one large chunk.\nWill allocate in smaller chunks\n");
}
}

memset(slabclass, 0, sizeof(slabclass));

while (++i < POWER_LARGEST && size <= POWER_BLOCK / 2) {
Expand Down Expand Up @@ -187,7 +205,7 @@ static int do_slabs_newslab(const unsigned int id) {

if (grow_slab_list(id) == 0) return 0;

ptr = malloc((size_t)len);
ptr = memory_allocate((size_t)len);
if (ptr == 0) return 0;

memset(ptr, 0, (size_t)len);
Expand Down Expand Up @@ -374,3 +392,32 @@ int do_slabs_reassign(unsigned char srcid, unsigned char dstid) {
return 1;
}
#endif

static void *memory_allocate(size_t size) {
void *ret;

if (mem_base == NULL) {
/* We are not using a preallocated large memory chunk */
ret = malloc(size);
} else {
ret = mem_current;

if (size > mem_avail) {
return NULL;
}

/* mem_current pointer _must_ be aligned!!! */
if (size % CHUNK_ALIGN_BYTES) {
size += CHUNK_ALIGN_BYTES - (size % CHUNK_ALIGN_BYTES);
}

mem_current += size;
if (size < mem_avail) {
mem_avail -= size;
} else {
mem_avail = 0;
}
}

return ret;
}
7 changes: 5 additions & 2 deletions slabs.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@

/** Init the subsystem. 1st argument is the limit on no. of bytes to allocate,
0 if no limit. 2nd argument is the growth factor; each slab will use a chunk
size equal to the previous slab's chunk size times this factor. */
void slabs_init(const size_t limit, const double factor);
size equal to the previous slab's chunk size times this factor.
3rd argument specifies if the slab allocator should allocate all memory
up front (if true), or allocate memory in chunks as it is needed (if false)
*/
void slabs_init(const size_t limit, const double factor, const bool prealloc);


/**
Expand Down

0 comments on commit a6b35b4

Please sign in to comment.