Skip to content

Commit

Permalink
checkpoint 2010-04-10
Browse files Browse the repository at this point in the history
  • Loading branch information
sorear committed Apr 11, 2010
1 parent 7e5122d commit 2e2bae2
Show file tree
Hide file tree
Showing 2 changed files with 121 additions and 73 deletions.
42 changes: 28 additions & 14 deletions src/Compact.h
Expand Up @@ -29,11 +29,22 @@ void *ac_unhandle(struct ac_handle_sort *kind, SV *value, const char *err);
SV *ac_rehandle(struct ac_handle_sort *kind, void *inner);

/*
* Identifies a single object. Need not actually be a pointer; our current
* storage manager uses them, though. I want to implement 32 bit index
* object IDs, though, for the sake of 64 bit platforms, eventually.
* Identifies a single object. Do not assume any particular representation
* of these, beyond that they are no larger than a UV.
*/
typedef void *ac_object;
typedef UV ac_object;

/* Only this many low bits of ac_object matter */
extern int ac_param_pointer_size;

/* He he he. I wonder how many compilers will decide the croak is not
reachable. */
#define AC_OVERFLOW_CHECK(x,y) \
STMT_START { \
if (((UV)(x) + (UV)(y)) < (UV)(x)) \
croak("Object is not constructable because its size would " \
"exceed the size of an unsigned integer."); \
}

/*
* A class consists of a type, some metadata controlling handles and allocation
Expand All @@ -53,6 +64,7 @@ typedef void *ac_object;
* fetch per object access.
*/
struct ac_type; /* forward */
union ac_page;
struct ac_class
{
struct ac_type *dtype;
Expand All @@ -61,12 +73,18 @@ struct ac_class
HV *stash; /* to bless handles */
int lifetime;

void *first_page;
void *last_page;
union ac_page **data_pages;
UV dpa_size;
UV num_data_pages;

int *dirents;
int num_dirents;
int dirent_ary_size;

UV total_objects;
UV total_pages;
UV obj_size_bytes;
UV obj_alloc_bits;

UV obj_size_bits;
UV obj_overhead_bits;

UV used_objects;
ac_object freelist_head;
Expand All @@ -87,7 +105,7 @@ struct ac_class
#define AC_LIFE_REF 3
#define AC_LIFE_REF8 4

struct ac_class *ac_new_class(struct ac_type *ty, UV nbytes, int lifetime,
struct ac_class *ac_new_class(struct ac_type *ty, UV nbits, int lifetime,
SV *metaclass, HV *stash);

ac_object ac_new_object(struct ac_class *cl);
Expand All @@ -102,10 +120,6 @@ ac_object ac_forward_object(ac_object o);
*/

/* These should not be assumed to work above 32 */
#define AC_IV_BIT (CHAR_BIT * sizeof(IV))
#define AC_NV_BIT (CHAR_BIT * sizeof(NV))
#define AC_U32_BIT (CHAR_BIT * sizeof(U32))
#define AC_U8_BIT (CHAR_BIT * sizeof(U8))
UV ac_object_fetch(ac_object o, UV bitoff, UV count);
IV ac_object_fetch_signed(ac_object o, UV bitoff, UV count);
/* does no value checking, deliberately */
Expand Down
152 changes: 93 additions & 59 deletions src/storage.c
Expand Up @@ -14,13 +14,15 @@
* premature time optimization :). Since an object is only a sequence of bits,
* we can do some interesting things with them.
*
* A single page (we currently hardcode 4K, same as hardware pages on x86, but
* it ultimately doesn't matter) contains 4096 byets, of which we reserve 16
* for a page header. Given an address into a page, you can get at the page
* header by bit manipulation of the address; this is crucial in the overhead
* reduction strategy, as it allows us to store type information once per 4KB
* instead of once per object, a huge savings for small objects. (Larger
* objects benefit from the bounding of fragmentation instead.)
* This version of the storage manager identifies objects by numbers. The top
* 20 bits of the identifier indexes into group_table, which is used to
* interleave identifier allocation between classes; once the classes' own
* number is obtained, it can be used to find the correct data page. Pages are
* 32,768 bits in size (incidentally the same as hardware pages on x86). This
* is crucial in the overhead reduction strategy, as it allows us to store type
* information once per 4KB instead of once per object, a huge savings for
* small objects. (Larger objects benefit from the bounding of fragmentation
* instead.)
*
* In general, an integral number of objects do not fit on one page. To avoid
* fragmentation, we put pages into an ordered sequence, and allow objects to
Expand All @@ -31,23 +33,51 @@
* TODO: Abstract the allocation logic and make it threadsafe.
*/

#define AC_PAGE_SIZE 4096 /* NOT getpagesize() */
#define AC_PAGE_BYTES 4096

struct page_header
{
struct ac_class *claz;
struct page_header *nextp;
struct page_header *prevp;
UV serialno;
union ac_page {
union ac_page *next;
char payload[AC_PAGE_BYTES];
};

struct page_header *free_page;
static union ac_page *free_page;

static void ac_push_free_page(union ac_page *pb)
{
pb->next = free_page;
free_page = pb;
}

static union ac_page *ac_get_free_page()
{
union ac_page *ret;

if (!free_page)
{
union ac_page *rq;
int i;

Newx(rq, 255, union ac_page);

for (i = 0; i < 255; i++)
ac_push_free_page(&rq[i]);
}

ret = free_page;
free_page = ret->next;
return free_page;
}

static void ac_delete_class(void *clp);

AC_DEFINE_HANDLE_SORT(class, 0, ac_delete_class, 0);

struct ac_class *ac_new_class(struct ac_type *ty, UV nbytes, int lifetime,
int ac_param_pointer_size = 32;

/* TODO Abstract the arena into an object - this, I think, will solve all the
annoying threading questions, and give us A::SC functionality for free */

struct ac_class *ac_new_class(struct ac_type *ty, UV nbits, int lifetime,
SV *metaclass, HV *stash)
{
struct ac_class *n;
Expand All @@ -63,67 +93,71 @@ struct ac_class *ac_new_class(struct ac_type *ty, UV nbytes, int lifetime,
SvREFCNT_inc((SV*)metaclass);
n->lifetime = lifetime;

n->obj_size_bytes = nbytes;
n->obj_overhead_bits =
(lifetime == AC_LIFE_REF8) ? 8 :
(lifetime == AC_LIFE_REF) ? 32 : 0;

n->obj_size_bits = nbits + n->obj_overhead_bits;

/* Leave room for the freelist pointer */
if (n->obj_size_bits < ac_param_pointer_size)
n->obj_size_bits = ac_param_pointer_size;

/* Prevent object count from reaching UV_MAX */
if (n->obj_size_bits < CHAR_BIT)
n->obj_size_bits = CHAR_BIT;

AC_OVERFLOW_CHECK(n->obj_size_bits, n->obj_overhead_bits);

return n;
}

struct ac_dirent
{
struct ac_class *cl;
int objnum;
};

#define OBJS_PER_DIRENT 8192
#define DIRENT_SHIFT 13

/* entry 0 is an unallocated sentinel */
static struct ac_dirent *directory;
static UV dirfree = 0;

void ac_delete_class(void *clp)
{
struct ac_class *cl = (struct ac_class *) cl;
struct page_header *ph, *nph;
int ix;

SvREFCNT_dec(cl->dtype->reflection);
SvREFCNT_dec(cl->metaclass);
SvREFCNT_dec((SV*)cl->stash);

ph = (struct page_header *)cl->first;

while (ph) {
nph = ph->nextp;
ph->nextp = free_page;
free_page = ph;
ph = nph;
}
}

static int allocsize = 8 * AC_PAGE_SIZE;

static void more_pages(void) {
#ifdef HAS_MMAP
struct page_header *newpages;
int offs;
Mmap_t mmr;

again:
mmr = mmap(0, allocsize, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE,
-1, 0);
for (ix = 0; ix < cl->num_data_pages; ix++)
ac_push_free_page(cl->data_pages[ix]);

if (mmr == MAP_FAILED && errno == EINVAL && allocsize)
for (ix = 0; ix < cl->num_dirents; ix++)
{
/* Automatically probe large page sizes */
allocsize <<= 1;
goto try_again;
directory[cl->dirents[ix]].objnum = dirfree;
dirfree = cl->dirents[ix];
}

if (mmr == MAP_FAILED)
{
croak("mmap failed: %s", strerror(errno));
}
Safefree(cl->data_pages);
Safefree(cl->dirents);
}

newpages = INT2PTR(struct page_header, mmr);
static void ac_push_free_obj(ac_object o) {
struct ac_class *cl = ac_class_of(o);

for (offs = 0; offs < allocsize; offs += AC_PAGE_SIZE)
{
struct page_header *np =
INT2PTR(struct page_header, PTR2UV(newpages) + offs);
np->nextp = free_page;
free_page = np;
}
ac_object_store(o, -ac_param_pointer_size,
ac_param_pointer_size, cl->freelist_head);
cl->freelist_head = o;
}

static void ac_refill(struct ac_class *cl) {
if (!free_page) more_pages();
static void ac_add_page(struct ac_class *cl) {
int old_complete_objects = cl->num_data_pages * AC_PAGE_BITS /
union ac_page *np = ac_get_free_page();
}

static void ac_destroy(ac_object o) {
Expand All @@ -132,8 +166,8 @@ static void ac_destroy(ac_object o) {
if (cl->dtype->flags & AC_DESTROY_USED)
cl->dtype->ops->destroy(cl->dtype, o, 0);

ac_object_store(o, 0, sizeof(UV) * CHAR_BIT, PTR2UV(cl->freelist_head));
cl->freelist_head = o;
ac_push_free_obj(o);

cl->used_objects--;
SvREFCNT_dec(cl->reflection);
}
Expand Down

0 comments on commit 2e2bae2

Please sign in to comment.