Commit e4ebe340 authored by Nick Mathewson's avatar Nick Mathewson 🏃
Browse files

r19049@catbus: nickm | 2008-03-26 12:33:25 -0400

 Add new stacklike, free-all-at-once memory allocation strategy.  Use it when parsing directory information.  This helps parsing speed, and may well help fragmentation some too.  hidden-service-related stuff still uses the old tokenizing strategies.


svn:r14194
parent 9dfd4132
......@@ -31,6 +31,10 @@ Changes in version 0.2.1.1-alpha - 2008-??-??
fixed in late 2006. Our new behavior is to call RAND_poll() at
startup, and to call RAND_poll() when we reseed later only if we
have a non-buggy OpenSSL version.
- Speed up parsing and cut down on memory fragmentation by using
stack-style allocations for parsing directory objects. Previously,
this accounted for over 40% of allocations from within Tor's code
on a typical directory cache.
- Lots of new unit tests.
o Code simplifications and refactoring:
......
......@@ -9,8 +9,8 @@ else
libor_extra_source=
endif
libor_a_SOURCES = log.c util.c compat.c container.c mempool.c \
libor_a_SOURCES = log.c util.c compat.c container.c mempool.c memarea.c \
$(libor_extra_source)
libor_crypto_a_SOURCES = crypto.c aes.c tortls.c torgzip.c
noinst_HEADERS = log.h crypto.h test.h util.h compat.h aes.h torint.h tortls.h strlcpy.c strlcat.c torgzip.h container.h ht.h mempool.h
noinst_HEADERS = log.h crypto.h test.h util.h compat.h aes.h torint.h tortls.h strlcpy.c strlcat.c torgzip.h container.h ht.h mempool.h memarea.h
/* Copyright (c) 2008, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/* $Id$ */
/** \file memarea.c
* \brief Implementation for memarea_t, an allocator for allocating lots of
* small objects that will be freed all at once.
*/
#include "orconfig.h"
#include <stdlib.h>
#include "memarea.h"
#include "util.h"
#include "compat.h"
#include "log.h"
/** All returned pointers should be aligned to the nearest multiple of this
* value. */
#define MEMAREA_ALIGN SIZEOF_VOID_P
#if MEMAREA_ALIGN == 4
#define MEMAREA_ALIGN_MASK 3lu
#elif MEMAREA_ALIGN == 8
#define MEMAREA_ALIGN_MASK 7lu
#else
#error "void* is neither 4 nor 8 bytes long. I don't know how to align stuff."
#endif
/* Increment <b>ptr</b> until it is aligned to MEMAREA_ALIGN. */
static INLINE void *
realign_pointer(void *ptr)
{
uintptr_t x = (uintptr_t)ptr;
x = (x+MEMAREA_ALIGN_MASK) & ~MEMAREA_ALIGN_MASK;
return (void*)x;
}
/** Implements part of a memarea. New memory is carved off from chunk->mem in
* increasing order until a request is too big, at which point a new chunk is
* allocated. */
typedef struct memarea_chunk_t {
/** Next chunk in this area. Only kept around so we can free it. */
struct memarea_chunk_t *next_chunk;
size_t mem_size; /**< How much RAM is available in u.mem, total? */
char *next_mem; /**< Next position in u.mem to allocate data at. If it's
* greater than or equal to mem+mem_size, this chunk is
* full. */
union {
char mem[1]; /**< Memory space in this chunk. */
void *_void_for_alignment; /**< Dummy; used to make sure mem is aligned. */
} u;
} memarea_chunk_t;
#define CHUNK_HEADER_SIZE STRUCT_OFFSET(memarea_chunk_t, u)
/** A memarea_t is an allocation region for a set of small memory requests
* that will all be freed at once. */
struct memarea_t {
struct memarea_chunk_t *first; /**< Top of the chunk stack: never NULL. */
size_t chunk_size; /**<Size to use when allocating chunks.*/
};
/** Helper: allocate a new memarea chunk of around <b>chunk_size</b> bytes. */
static memarea_chunk_t *
alloc_chunk(size_t chunk_size)
{
memarea_chunk_t *res = tor_malloc_roundup(&chunk_size);
res->next_chunk = NULL;
res->mem_size = chunk_size - CHUNK_HEADER_SIZE;
res->next_mem = res->u.mem;
return res;
}
/** Allocate and return new memarea, with chunks of approximately
* <b>chunk_size</b> bytes. (There is indeed some overhead.) */
memarea_t *
memarea_new(size_t chunk_size)
{
memarea_t *head = tor_malloc(sizeof(memarea_t));
head->first = alloc_chunk(chunk_size);
head->chunk_size = chunk_size;
return head;
}
/** Free <b>area</b>, invalidating all pointers returned from memarea_alloc()
* and friends for this area */
void
memarea_drop_all(memarea_t *area)
{
memarea_chunk_t *chunk, *next;
for (chunk = area->first; chunk; chunk = next) {
next = chunk->next_chunk;
tor_free(chunk);
}
area->first = NULL; /*fail fast on */
tor_free(area);
}
/** Forget about having allocated anything in <b>area</b>, and free some of
* the backing storage associated with it, as appropriate. Invalidates all
* pointers returned from memarea_alloc() for this area. */
void
memarea_clear(memarea_t *area)
{
memarea_chunk_t *chunk, *next;
if (area->first->next_chunk) {
for (chunk = area->first->next_chunk; chunk; chunk = next) {
next = chunk->next_chunk;
tor_free(chunk);
}
area->first->next_chunk = NULL;
}
area->first->next_mem = area->first->u.mem;
}
/** Return true iff <b>p</b> is in a range that has been returned by an
* allocation from <b>area</b>. */
int
memarea_owns_ptr(const memarea_t *area, const void *p)
{
memarea_chunk_t *chunk;
const char *ptr = p;
for (chunk = area->first; chunk; chunk = chunk->next_chunk) {
if (ptr >= chunk->u.mem && ptr < chunk->next_mem)
return 1;
}
return 0;
}
/** Return a pointer to a chunk of memory in <b>area</b> of at least <b>sz</b>
* bytes. <b>sz</b> should be significantly smaller than the area's chunk
* size, though we can deal if it isn't. */
void *
memarea_alloc(memarea_t *area, size_t sz)
{
memarea_chunk_t *chunk = area->first;
char *result;
tor_assert(chunk);
if (chunk->next_mem+sz > chunk->u.mem+chunk->mem_size) {
if (sz+CHUNK_HEADER_SIZE >= area->chunk_size) {
/* This allocation is too big. Stick it in a special chunk, and put
* that chunk second in the list. */
memarea_chunk_t *new_chunk = alloc_chunk(sz+CHUNK_HEADER_SIZE);
new_chunk->next_chunk = chunk->next_chunk;
chunk->next_chunk = new_chunk;
chunk = new_chunk;
} else {
memarea_chunk_t *new_chunk = alloc_chunk(area->chunk_size);
new_chunk->next_chunk = chunk;
area->first = chunk = new_chunk;
}
tor_assert(chunk->mem_size >= sz);
}
result = chunk->next_mem;
chunk->next_mem = realign_pointer(chunk->next_mem + sz);
return result;
}
/** As memarea_alloc(), but clears the memory it returns. */
void *
memarea_alloc_zero(memarea_t *area, size_t sz)
{
void *result = memarea_alloc(area, sz);
memset(result, 0, sz);
return result;
}
/** As memdup, but returns the memory from <b>area</b>. */
void *
memarea_memdup(memarea_t *area, const void *s, size_t n)
{
char *result = memarea_alloc(area, n);
memcpy(result, s, n);
return result;
}
/** As strdup, but returns the memory from <b>area</b>. */
char *
memarea_strdup(memarea_t *area, const char *s)
{
return memarea_memdup(area, s, strlen(s)+1);
}
/** As strndup, but returns the memory from <b>area</b>. */
char *
memarea_strndup(memarea_t *area, const char *s, size_t n)
{
size_t ln;
char *result;
const char *cp, *end = s+n;
for (cp = s; *cp && cp < end; ++cp)
;
/* cp now points to s+n, or to the 0 in the string. */
ln = cp-s;
result = memarea_memdup(area, s, ln+1);
result[ln]='\0';
return result;
}
/** Assert that <b>area</b> is okay. */
void
memarea_assert_ok(memarea_t *area)
{
memarea_chunk_t *chunk;
tor_assert(area->first);
for (chunk = area->first; chunk; chunk = chunk->next_chunk) {
tor_assert(chunk->next_mem >= chunk->u.mem);
tor_assert(chunk->next_mem <= chunk->u.mem+chunk->mem_size+MEMAREA_ALIGN);
}
}
/* Copyright (c) 2008, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/* $Id$ */
/* Tor dependencies */
#ifndef MEMAREA_H
#define MEMAREA_H
typedef struct memarea_t memarea_t;
memarea_t *memarea_new(size_t chunk_size);
void memarea_drop_all(memarea_t *area);
void memarea_clear(memarea_t *area);
int memarea_owns_ptr(const memarea_t *area, const void *ptr);
void *memarea_alloc(memarea_t *area, size_t sz);
void *memarea_alloc_zero(memarea_t *area, size_t sz);
void *memarea_memdup(memarea_t *area, const void *s, size_t n);
char *memarea_strdup(memarea_t *area, const char *s);
char *memarea_strndup(memarea_t *area, const char *s, size_t n);
void memarea_assert_ok(memarea_t *area);
#endif
This diff is collapsed.
......@@ -42,6 +42,7 @@ const char tor_svn_revision[] = "";
#include "test.h"
#include "torgzip.h"
#include "mempool.h"
#include "memarea.h"
int have_failed = 0;
......@@ -3364,6 +3365,97 @@ test_util_mempool(void)
smartlist_free(allocated);
}
static void
test_util_memarea(void)
{
memarea_t *area = memarea_new(1024);
char *p1, *p2, *p3, *p1_orig;
int i;
test_assert(area);
p1_orig = p1 = memarea_alloc(area,64);
p2 = memarea_alloc_zero(area,52);
p3 = memarea_alloc(area,11);
test_assert(memarea_owns_ptr(area, p1));
test_assert(memarea_owns_ptr(area, p2));
test_assert(memarea_owns_ptr(area, p3));
/* Make sure we left enough space. */
test_assert(p1+64 <= p2);
test_assert(p2+52 <= p3);
/* Make sure we aligned. */
test_eq(((uintptr_t)p1) % sizeof(void*), 0);
test_eq(((uintptr_t)p2) % sizeof(void*), 0);
test_eq(((uintptr_t)p3) % sizeof(void*), 0);
test_assert(!memarea_owns_ptr(area, p3+8192));
test_assert(!memarea_owns_ptr(area, p3+30));
test_assert(tor_mem_is_zero(p2, 52));
/* Make sure we don't overalign. */
p1 = memarea_alloc(area, 1);
p2 = memarea_alloc(area, 1);
test_eq(p1+sizeof(void*), p2);
{
void *ptr = tor_malloc(64);
test_assert(!memarea_owns_ptr(area, ptr));
tor_free(ptr);
}
/* memarea_memdup */
{
char *ptr = tor_malloc(64);
crypto_rand(ptr, 64);
p1 = memarea_memdup(area, ptr, 64);
test_assert(p1 != ptr);
test_memeq(p1, ptr, 64);
tor_free(ptr);
}
/* memarea_strdup. */
p1 = memarea_strdup(area,"");
p2 = memarea_strdup(area, "abcd");
test_assert(p1);
test_assert(p2);
test_streq(p1, "");
test_streq(p2, "abcd");
/* memarea_strndup. */
{
const char *s = "Ad ogni porta batte la morte e grida: il nome!";
/* (From Turandot, act 3.) */
size_t len = strlen(s);
p1 = memarea_strndup(area, s, 1000);
p2 = memarea_strndup(area, s, 10);
test_streq(p1, s);
test_assert(p2 >= p1 + len + 1);
test_memeq(s, p2, 10);
test_eq(p2[10], '\0');
p3 = memarea_strndup(area, s, len);
test_streq(p3, s);
p3 = memarea_strndup(area, s, len-1);
test_memeq(s, p3, len-1);
test_eq(p3[len-1], '\0');
}
memarea_clear(area);
p1 = memarea_alloc(area, 1);
test_eq(p1, p1_orig);
memarea_clear(area);
/* Check for running over an area's size. */
for (i = 0; i < 512; ++i) {
p1 = memarea_alloc(area, crypto_rand_int(5)+1);
test_assert(memarea_owns_ptr(area, p1));
}
memarea_assert_ok(area);
/* Make sure we can allocate a too-big object. */
p1 = memarea_alloc_zero(area, 9000);
p2 = memarea_alloc_zero(area, 16);
test_assert(memarea_owns_ptr(area, p1));
test_assert(memarea_owns_ptr(area, p2));
memarea_drop_all(area);
}
static void
test_util_datadir(void)
{
......@@ -3715,6 +3807,7 @@ static struct {
SUBENT(util, smartlist),
SUBENT(util, bitarray),
SUBENT(util, mempool),
SUBENT(util, memarea),
SUBENT(util, strmap),
SUBENT(util, control_formats),
SUBENT(util, pqueue),
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment