Browse Source

r12706@catbus: nickm | 2007-05-09 18:39:46 -0400
Keep two freelists for buffer ram chunks: one of 4k chunks, and one of 16k chunks. Also, document the whole business.


svn:r10150

Nick Mathewson 18 years ago
parent
commit
34a09c24b5
4 changed files with 105 additions and 75 deletions
  1. 3 3
      ChangeLog
  2. 100 70
      src/or/buffers.c
  3. 1 1
      src/or/main.c
  4. 1 1
      src/or/or.h

+ 3 - 3
ChangeLog

@@ -38,9 +38,9 @@ Changes in version 0.2.0.1-alpha - 2007-??-??
       internally and transfer the data in-process.  This saves two
       internally and transfer the data in-process.  This saves two
       sockets per anonymous directory connection (at the client and at
       sockets per anonymous directory connection (at the client and at
       the server), and avoids the nasty Windows socketpair() workaround.
       the server), and avoids the nasty Windows socketpair() workaround.
-    - Keep unused 4k buffers on a free list, rather than wasting 8k for every
+    - Keep unused 4k and 16k buffers on free lists, rather than wasting 8k
-      single inactive connection_t.
+      for every single inactive connection_t.
-    - Free items from the 4k-buffer free list when they haven't been used
+    - Free items from the 4/16k-buffer free lists when they haven't been used
       for a while.
       for a while.
 
 
   o Minor features (build):
   o Minor features (build):

+ 100 - 70
src/or/buffers.c

@@ -158,80 +158,122 @@ _split_range(buf_t *buf, char *at, size_t *len,
   }
   }
 }
 }
 
 
-/** DOCDOC */
+/** A freelist of buffer RAM chunks. */
-static char *free_mem_list = NULL;
+typedef struct free_mem_list_t {
-static int free_mem_list_len = 0;
+  char *list; /**< The first item on the list; begins with pointer to the
-static int free_mem_list_lowwater = 0;
+               * next item. */
+  int len; /**< How many entries in <b>list</b>. */
+  int lowwater; /**< The smallest that list has gotten since the last call to
+                 * buf_shrink_freelists(). */
+  const size_t chunksize; /**< How big are the items on the list? */
+  const int slack; /**< We always keep at least this many items on the list
+                    * when shrinking it. */
+  const int max; /**< How many elements are we willing to throw onto the list?
+                  */
+} free_mem_list_t;
+
+/** Freelists to hold 4k and 16k memory chunks.  This seems to be what
+ * we use most. */
+static free_mem_list_t free_mem_list_4k = { NULL, 0, 0, 4096, 16, INT_MAX };
+static free_mem_list_t free_mem_list_16k = { NULL, 0, 0, 16384, 4, 128 };
+
+/** Macro: True iff the size is one for which we keep a freelist. */
+#define IS_FREELIST_SIZE(sz) ((sz) == 4096 || (sz) == 16384)
+
+/** Return the proper freelist for chunks of size <b>sz</b>, or fail
+ * with an assertion. */
+static INLINE free_mem_list_t *
+get_free_mem_list(size_t sz)
+{
+  if (sz == 4096) {
+    return &free_mem_list_4k;
+  } else {
+    tor_assert(sz == 16384);
+    return &free_mem_list_16k;
+  }
+}
 
 
-/** DOCDOC */
+/** Throw the memory from <b>buf</b> onto the appropriate freelist.
-static void
+ * Return true if we added the memory, 0 if the freelist was full. */
+static int
 add_buf_mem_to_freelist(buf_t *buf)
 add_buf_mem_to_freelist(buf_t *buf)
 {
 {
   char *mem;
   char *mem;
+  free_mem_list_t *list;
 
 
-  tor_assert(buf->len == MIN_LAZY_SHRINK_SIZE);
   tor_assert(buf->datalen == 0);
   tor_assert(buf->datalen == 0);
   tor_assert(buf->mem);
   tor_assert(buf->mem);
+  list = get_free_mem_list(buf->len);
+
+  if (list->len >= list->max)
+    return 0;
 
 
   mem = RAW_MEM(buf->mem);
   mem = RAW_MEM(buf->mem);
   buf->len = buf->memsize = 0;
   buf->len = buf->memsize = 0;
   buf->mem = buf->cur = NULL;
   buf->mem = buf->cur = NULL;
 
 
-  *(char**)mem = free_mem_list;
+  *(char**)mem = list->list;
-  free_mem_list = mem;
+  list->list = mem;
-  ++free_mem_list_len;
+  ++list->len;
-  log_info(LD_GENERAL, "Add buf mem to freelist.  Freelist has %d entries.",
+  log_debug(LD_GENERAL, "Add buf mem to %d-byte freelist.  Freelist has "
-           free_mem_list_len);
+            "%d entries.", (int)list->chunksize, list->len);
+
+  return 1;
 }
 }
 
 
-/** DOCDOC */
+/** Pull memory of size <b>sz</b> from the appropriate freelist for use by
+ * <b>buf</b>, or allocate it as needed. */
 static void
 static void
-buf_get_initial_mem(buf_t *buf)
+buf_get_initial_mem(buf_t *buf, size_t sz)
 {
 {
   char *mem;
   char *mem;
+  free_mem_list_t *list = get_free_mem_list(sz);
   tor_assert(!buf->mem);
   tor_assert(!buf->mem);
 
 
-  if (free_mem_list) {
+  if (list->list) {
-    mem = free_mem_list;
+    mem = list->list;
-    free_mem_list = *(char**)mem;
+    list->list = *(char**)mem;
-    if (--free_mem_list_len < free_mem_list_lowwater)
+    if (--list->len < list->lowwater)
-      free_mem_list_lowwater = free_mem_list_len;
+      list->lowwater = list->len;
-    log_info(LD_GENERAL, "Got buf mem from freelist. Freelist has %d entries.",
+    log_debug(LD_GENERAL, "Got buf mem from %d-byte freelist. Freelist has "
-             free_mem_list_len);
+             "%d entries.", (int)list->chunksize, list->len);
   } else {
   } else {
-    log_info(LD_GENERAL, "Freelist empty; allocating another chunk.");
+    log_debug(LD_GENERAL, "%d-byte freelist empty; allocating another chunk.",
-    tor_assert(free_mem_list_len == 0);
+             (int)list->chunksize);
-    mem = tor_malloc(ALLOC_LEN(MIN_LAZY_SHRINK_SIZE));
+    tor_assert(list->len == 0);
+    mem = tor_malloc(ALLOC_LEN(sz));
   }
   }
   buf->mem = GUARDED_MEM(mem);
   buf->mem = GUARDED_MEM(mem);
-  SET_GUARDS(buf->mem, MIN_LAZY_SHRINK_SIZE);
+  SET_GUARDS(buf->mem, sz);
-  buf->len = MIN_LAZY_SHRINK_SIZE;
+  buf->len = sz;
-  buf->memsize = ALLOC_LEN(MIN_LAZY_SHRINK_SIZE);
+  buf->memsize = ALLOC_LEN(sz);
   buf->cur = buf->mem;
   buf->cur = buf->mem;
 }
 }
 
 
-/** DOCDOC 64k of 4k buffers. */
+/** Remove elements from the freelists that haven't been needed since the
-#define BUF_FREELIST_SLACK 16
+ * last call to this function. */
-
-/** DOCDOC */
 void
 void
-buf_shrink_freelist(void)
+buf_shrink_freelists(void)
 {
 {
-  if (free_mem_list_lowwater > BUF_FREELIST_SLACK) {
+  int j;
-    int i;
+  for (j = 0; j < 2; ++j) {
-    log_info(LD_GENERAL, "We haven't used %d/%d allocated buffer memory "
+    free_mem_list_t *list = j ? &free_mem_list_16k : &free_mem_list_4k;
-             "chunks since the last call(); freeing all but %d of them",
+    if (list->lowwater > list->slack) {
-             free_mem_list_lowwater, free_mem_list_len,
+      int i;
-             BUF_FREELIST_SLACK);
+      log_info(LD_GENERAL, "We haven't used %d/%d allocated %d-byte buffer "
-    for (i = BUF_FREELIST_SLACK; i < free_mem_list_lowwater; ++i) {
+               "memory chunks since the last call; freeing all but %d of them",
-      char *mem = free_mem_list;
+               list->lowwater, list->len, list->chunksize, list->slack);
-      tor_assert(mem);
+      for (i = list->slack; i < list->lowwater; ++i) {
-      free_mem_list = *(char**)mem;
+        /* XXXX we should really free the last few entries, not the first. */
-      tor_free(mem);
+        char *mem = list->list;
-      --free_mem_list_len;
+        tor_assert(mem);
+        list->list = *(char**)mem;
+        tor_free(mem);
+        --list->len;
+      }
     }
     }
+    list->lowwater = list->len;
   }
   }
-  free_mem_list_lowwater = free_mem_list_len;
 }
 }
 
 
 /** Change a buffer's capacity. <b>new_capacity</b> must be \>=
 /** Change a buffer's capacity. <b>new_capacity</b> must be \>=
@@ -281,26 +323,12 @@ buf_resize(buf_t *buf, size_t new_capacity)
     }
     }
   }
   }
 
 
-#if 0
+  if (new_capacity < MIN_LAZY_SHRINK_SIZE)
-  /* XXX Some play code to throw away old buffers sometimes rather
-   * than constantly reallocing them; just in case this is our memory
-   * problem. It looks for now like it isn't, so disabled. -RD */
-  if (0 && new_capacity == MIN_LAZY_SHRINK_SIZE &&
-      !buf->datalen &&
-      buf->len >= 1<<16) {
-    /* don't realloc; free and malloc */
-    char *oldmem, *newmem = GUARDED_MEM(tor_malloc(ALLOC_LEN(new_capacity)));
-    SET_GUARDS(newmem, new_capacity);
-    oldmem = RAW_MEM(buf->mem);
-    tor_free(oldmem);
-    buf->mem = buf->cur = newmem;
-  } else { /* ... */ }
-#endif
-
-  if (buf->len == 0 && new_capacity <= MIN_LAZY_SHRINK_SIZE) {
     new_capacity = MIN_LAZY_SHRINK_SIZE;
     new_capacity = MIN_LAZY_SHRINK_SIZE;
+
+  if (buf->len == 0 && IS_FREELIST_SIZE(new_capacity)) {
     tor_assert(!buf->mem);
     tor_assert(!buf->mem);
-    buf_get_initial_mem(buf);
+    buf_get_initial_mem(buf, new_capacity);
   } else {
   } else {
     char *raw;
     char *raw;
     if (buf->mem)
     if (buf->mem)
@@ -385,9 +413,9 @@ buf_shrink(buf_t *buf)
 
 
   new_len = buf->len;
   new_len = buf->len;
   if (buf->datalen == 0 && buf->highwater == 0 &&
   if (buf->datalen == 0 && buf->highwater == 0 &&
-      buf->len == MIN_LAZY_SHRINK_SIZE) {
+      IS_FREELIST_SIZE(buf->len)) {
-    add_buf_mem_to_freelist(buf);
+    if (add_buf_mem_to_freelist(buf))
-    return;
+      return;
   }
   }
   while (buf->highwater < (new_len>>2) && new_len > MIN_LAZY_SHRINK_SIZE*2)
   while (buf->highwater < (new_len>>2) && new_len > MIN_LAZY_SHRINK_SIZE*2)
     new_len >>= 1;
     new_len >>= 1;
@@ -433,8 +461,8 @@ buf_new_with_capacity(size_t size)
   buf_t *buf;
   buf_t *buf;
   buf = tor_malloc_zero(sizeof(buf_t));
   buf = tor_malloc_zero(sizeof(buf_t));
   buf->magic = BUFFER_MAGIC;
   buf->magic = BUFFER_MAGIC;
-  if (size == MIN_LAZY_SHRINK_SIZE) {
+  if (IS_FREELIST_SIZE(size)) {
-    buf_get_initial_mem(buf);
+    buf_get_initial_mem(buf, size);
   } else {
   } else {
     buf->cur = buf->mem = GUARDED_MEM(tor_malloc(ALLOC_LEN(size)));
     buf->cur = buf->mem = GUARDED_MEM(tor_malloc(ALLOC_LEN(size)));
     SET_GUARDS(buf->mem, size);
     SET_GUARDS(buf->mem, size);
@@ -492,10 +520,12 @@ buf_free(buf_t *buf)
   char *oldmem;
   char *oldmem;
   assert_buf_ok(buf);
   assert_buf_ok(buf);
   buf->magic = 0xDEADBEEF;
   buf->magic = 0xDEADBEEF;
-  if (buf->len == MIN_LAZY_SHRINK_SIZE) {
+  if (IS_FREELIST_SIZE(buf->len)) {
     buf->datalen = 0; /* Avoid assert in add_buf_mem_to_freelist. */
     buf->datalen = 0; /* Avoid assert in add_buf_mem_to_freelist. */
     add_buf_mem_to_freelist(buf);
     add_buf_mem_to_freelist(buf);
-  } else if (buf->mem) {
+  }
+  if (buf->mem) {
+    /* The freelist didn't want the RAM. */
     oldmem = RAW_MEM(buf->mem);
     oldmem = RAW_MEM(buf->mem);
     tor_free(oldmem);
     tor_free(oldmem);
   }
   }

+ 1 - 1
src/or/main.c

@@ -1021,7 +1021,7 @@ run_scheduled_events(time_t now)
         buf_shrink(conn->inbuf);
         buf_shrink(conn->inbuf);
     }
     }
     clean_cell_pool();
     clean_cell_pool();
-    buf_shrink_freelist();
+    buf_shrink_freelists();
     time_to_shrink_memory = now + MEM_SHRINK_INTERVAL;
     time_to_shrink_memory = now + MEM_SHRINK_INTERVAL;
   }
   }
 
 

+ 1 - 1
src/or/or.h

@@ -2020,7 +2020,7 @@ buf_t *buf_new_with_capacity(size_t size);
 void buf_free(buf_t *buf);
 void buf_free(buf_t *buf);
 void buf_clear(buf_t *buf);
 void buf_clear(buf_t *buf);
 void buf_shrink(buf_t *buf);
 void buf_shrink(buf_t *buf);
-void buf_shrink_freelist(void);
+void buf_shrink_freelists(void);
 
 
 size_t buf_datalen(const buf_t *buf);
 size_t buf_datalen(const buf_t *buf);
 size_t buf_capacity(const buf_t *buf);
 size_t buf_capacity(const buf_t *buf);