Refactor into a GC library (gc.c and gc.h) and a test program (gc_test.c).
Also, allocate GC memory ranges dynamically during startup (gc_init()) rather than statically.
This commit is contained in:
parent
61b83a1293
commit
8db40406a0
22
Makefile
22
Makefile
|
|
@ -1,18 +1,30 @@
|
||||||
CFLAGS = -std=c99 -O2 -DNDEBUG
|
CFLAGS = -std=c99
|
||||||
|
|
||||||
all: gc
|
ifeq ($(DEBUG),yes)
|
||||||
|
CFLAGS += -g
|
||||||
|
PROFILE = no
|
||||||
|
dummy := $(shell $(MAKE) clean)
|
||||||
|
else
|
||||||
|
CFLAGS += -O2 -DNDEBUG
|
||||||
|
endif
|
||||||
|
|
||||||
|
all: gc_test
|
||||||
.PHONY: all clean
|
.PHONY: all clean
|
||||||
|
|
||||||
ifneq ($(PROFILE),no)
|
ifneq ($(PROFILE),no)
|
||||||
CFLAGS += -fprofile-generate
|
CFLAGS += -fprofile-generate
|
||||||
|
LDFLAGS += -fprofile-generate
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifneq (,$(wildcard *.gcda))
|
ifneq (,$(wildcard *.gcda))
|
||||||
CFLAGS += -fprofile-use
|
CFLAGS += -fprofile-use
|
||||||
dummy := $(shell rm -f gc)
|
dummy := $(shell rm -f gc_test *.o)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
-rm -f gc gc.gcda gc.gcno
|
-rm -f gc_test *.o *.gcda *.gcno
|
||||||
|
|
||||||
gc: gc.c
|
gc_test: gc_test.o gc.o
|
||||||
|
|
||||||
|
gc_test.o: gc_test.c gc.h
|
||||||
|
gc.o: gc.c gc.h
|
||||||
|
|
|
||||||
244
gc.c
244
gc.c
|
|
@ -1,5 +1,3 @@
|
||||||
#include <sys/time.h>
|
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
@ -8,115 +6,13 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#include "gc.h"
|
||||||
# define debug(printf_args) ((void)printf printf_args)
|
|
||||||
#else
|
|
||||||
# define debug(printf_args) ((void)0)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef uintptr_t value_t;
|
extern int gc_counter;
|
||||||
|
extern int gc_ticks;
|
||||||
|
|
||||||
/* NIL: 00000000 00000000 00000000 00000000 */
|
/* Alignment must ensure each object has enough room to hold a pair (BH . new_addr) */
|
||||||
/* Pair: aaaaaaaa aaaaaaaa aaaaaaaa aaaaaa00 (where aa... >= 1024) */
|
#define GC_ALIGNMENT ((size_t)(sizeof(pair_t)))
|
||||||
/* Object: aaaaaaaa aaaaaaaa aaaaaaaa aaaaaa10 */
|
|
||||||
/* Fixnum: snnnnnnn nnnnnnnn nnnnnnnn nnnnnnn1 */
|
|
||||||
|
|
||||||
|
|
||||||
/* Special values (0 <= n < 1024) */
|
|
||||||
/* These correspond to pairs within the first page of memory */
|
|
||||||
#define SPECIAL_VALUE(n) ((value_t)(4*n))
|
|
||||||
#define MAX_SPECIAL SPECIAL_VALUE(1023)
|
|
||||||
|
|
||||||
#define NIL SPECIAL_VALUE(0)
|
|
||||||
#define BROKEN_HEART SPECIAL_VALUE(1)
|
|
||||||
#define TYPE_TAG_BOX SPECIAL_VALUE(2)
|
|
||||||
#define TYPE_TAG_VECTOR SPECIAL_VALUE(3)
|
|
||||||
#define TYPE_TAG_BYTESTR SPECIAL_VALUE(4)
|
|
||||||
|
|
||||||
typedef struct pair
|
|
||||||
{
|
|
||||||
value_t car;
|
|
||||||
value_t cdr;
|
|
||||||
} pair_t;
|
|
||||||
|
|
||||||
typedef struct object
|
|
||||||
{
|
|
||||||
value_t tag;
|
|
||||||
union {
|
|
||||||
value_t values[0];
|
|
||||||
char bytes[0];
|
|
||||||
} payload;
|
|
||||||
} object_t;
|
|
||||||
|
|
||||||
typedef struct vector
|
|
||||||
{
|
|
||||||
value_t tag;
|
|
||||||
size_t size;
|
|
||||||
value_t elements[0];
|
|
||||||
} vector_t;
|
|
||||||
|
|
||||||
typedef struct byte_string
|
|
||||||
{
|
|
||||||
value_t tag;
|
|
||||||
size_t size;
|
|
||||||
uint8_t bytes[0];
|
|
||||||
} byte_string_t;
|
|
||||||
|
|
||||||
typedef struct gc_root
|
|
||||||
{
|
|
||||||
value_t value;
|
|
||||||
struct gc_root *prev;
|
|
||||||
struct gc_root *next;
|
|
||||||
} gc_root_t;
|
|
||||||
|
|
||||||
static inline bool is_pair(value_t v)
|
|
||||||
{
|
|
||||||
return ((v & 0x3) == 0) && (v > MAX_SPECIAL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool is_object(value_t v)
|
|
||||||
{
|
|
||||||
return ((v & 0x1) == 0) && (v > MAX_SPECIAL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool is_fixnum(value_t v)
|
|
||||||
{
|
|
||||||
return (v & 1) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline value_t to_fixnum(intptr_t n)
|
|
||||||
{
|
|
||||||
return (value_t)(n << 1) | 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline intptr_t from_fixnum(value_t n)
|
|
||||||
{
|
|
||||||
return ((intptr_t)n) >> 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline value_t pair_value(pair_t *p)
|
|
||||||
{
|
|
||||||
return (value_t)p;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline value_t object_value(void *obj)
|
|
||||||
{
|
|
||||||
return (value_t)obj | 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
object_t *get_object(value_t v);
|
|
||||||
pair_t *get_pair(value_t pair);
|
|
||||||
value_t get_cdr(value_t pair);
|
|
||||||
void replace_car(value_t pair, value_t car);
|
|
||||||
void replace_cdr(value_t pair, value_t cdr);
|
|
||||||
value_t cons(value_t car, value_t cdr);
|
|
||||||
|
|
||||||
void register_gc_root(gc_root_t *root, value_t v);
|
|
||||||
void unregister_gc_root(gc_root_t *root);
|
|
||||||
void *gc_alloc(size_t nbytes);
|
|
||||||
void collect_garbage(size_t min_free);
|
|
||||||
|
|
||||||
/***********************************************/
|
|
||||||
|
|
||||||
/* Pairs are a type of object, but the value representation is different */
|
/* Pairs are a type of object, but the value representation is different */
|
||||||
object_t *get_object(value_t v)
|
object_t *get_object(value_t v)
|
||||||
|
|
@ -130,13 +26,11 @@ object_t *get_object(value_t v)
|
||||||
pair_t *get_pair(value_t v)
|
pair_t *get_pair(value_t v)
|
||||||
{
|
{
|
||||||
if (is_pair(v))
|
if (is_pair(v))
|
||||||
return (pair_t*)v;
|
return (pair_t*)(v - 2);
|
||||||
else
|
else
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
static int cons_counter = 0;
|
|
||||||
|
|
||||||
value_t cons(value_t car, value_t cdr)
|
value_t cons(value_t car, value_t cdr)
|
||||||
{
|
{
|
||||||
gc_root_t car_root, cdr_root;
|
gc_root_t car_root, cdr_root;
|
||||||
|
|
@ -152,26 +46,23 @@ value_t cons(value_t car, value_t cdr)
|
||||||
unregister_gc_root(&car_root);
|
unregister_gc_root(&car_root);
|
||||||
unregister_gc_root(&cdr_root);
|
unregister_gc_root(&cdr_root);
|
||||||
|
|
||||||
++cons_counter;
|
|
||||||
|
|
||||||
return pair_value(p);
|
return pair_value(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Equal to the max. active set size. */
|
static char *gc_ranges[2];
|
||||||
#define GC_RANGE_SIZE (256*1024*1024)
|
static size_t gc_min_size;
|
||||||
|
static size_t gc_max_size;
|
||||||
|
static size_t gc_soft_limit;
|
||||||
|
|
||||||
/* GC starts after this much space has been allocated. Increases by 5% each time. */
|
static int gc_current_range;
|
||||||
#define GC_INIT_SIZE 1024
|
static size_t gc_free_space;
|
||||||
|
static char *gc_free_ptr;
|
||||||
|
|
||||||
/* Alignment must ensure each object has enough room to hold a pair (BH . new_addr) */
|
static gc_root_t gc_root_list = {
|
||||||
#define GC_ALIGNMENT ((size_t)(sizeof(pair_t)))
|
.value = NIL,
|
||||||
|
.prev = &gc_root_list,
|
||||||
static pair_t gc_ranges[2][GC_RANGE_SIZE / sizeof(pair_t)];
|
.next = &gc_root_list
|
||||||
static char *gc_free_ptr = (char*)&gc_ranges[0][0];
|
};
|
||||||
static size_t gc_free_space = GC_INIT_SIZE;
|
|
||||||
static size_t gc_soft_limit = GC_INIT_SIZE;
|
|
||||||
static int current_gc_range = 0;
|
|
||||||
static gc_root_t gc_root_list = { NIL, &gc_root_list, &gc_root_list };
|
|
||||||
|
|
||||||
static inline size_t gc_align(size_t nbytes) __attribute__ ((const));
|
static inline size_t gc_align(size_t nbytes) __attribute__ ((const));
|
||||||
static int gc_range_of(void *object) __attribute__ ((const));
|
static int gc_range_of(void *object) __attribute__ ((const));
|
||||||
|
|
@ -194,6 +85,24 @@ static int gc_range_of(void *object)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void gc_init(size_t min_size, size_t max_size)
|
||||||
|
{
|
||||||
|
assert(min_size <= max_size);
|
||||||
|
|
||||||
|
gc_ranges[0] = (char*)malloc(max_size);
|
||||||
|
gc_ranges[1] = (char*)malloc(max_size);
|
||||||
|
|
||||||
|
assert(gc_ranges[0] && gc_ranges[1]);
|
||||||
|
|
||||||
|
gc_current_range = 0;
|
||||||
|
gc_free_ptr = gc_ranges[gc_current_range];
|
||||||
|
|
||||||
|
gc_min_size = min_size;
|
||||||
|
gc_max_size = max_size;
|
||||||
|
gc_soft_limit = gc_min_size;
|
||||||
|
gc_free_space = gc_soft_limit;
|
||||||
|
}
|
||||||
|
|
||||||
void register_gc_root(gc_root_t *root, value_t v)
|
void register_gc_root(gc_root_t *root, value_t v)
|
||||||
{
|
{
|
||||||
root->value = v;
|
root->value = v;
|
||||||
|
|
@ -216,13 +125,6 @@ void unregister_gc_root(gc_root_t *root)
|
||||||
root->prev = root->next = root;
|
root->prev = root->next = root;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void out_of_memory(void) __attribute__ ((noreturn));
|
|
||||||
static void out_of_memory(void)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Out of memory!\n");
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
void *gc_alloc(size_t nbytes)
|
void *gc_alloc(size_t nbytes)
|
||||||
{
|
{
|
||||||
nbytes = gc_align(nbytes);
|
nbytes = gc_align(nbytes);
|
||||||
|
|
@ -244,7 +146,7 @@ static void transfer_object(value_t *value)
|
||||||
object_t *obj = get_object(*value);
|
object_t *obj = get_object(*value);
|
||||||
value_t new_value;
|
value_t new_value;
|
||||||
|
|
||||||
assert(gc_range_of(obj) != current_gc_range);
|
assert(gc_range_of(obj) != gc_current_range);
|
||||||
|
|
||||||
if (obj->tag == BROKEN_HEART)
|
if (obj->tag == BROKEN_HEART)
|
||||||
{
|
{
|
||||||
|
|
@ -296,10 +198,10 @@ static void transfer_object(value_t *value)
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
if (is_pair(new_value))
|
if (is_pair(new_value))
|
||||||
{
|
{
|
||||||
if (gc_range_of(get_pair(new_value)) != current_gc_range)
|
if (gc_range_of(get_pair(new_value)) != gc_current_range)
|
||||||
{
|
{
|
||||||
debug(("Invalid address after transfer: 0x%0.8X. Current GC: %d.\n",
|
debug(("Invalid address after transfer: 0x%0.8X. Current GC: %d.\n",
|
||||||
get_pair(new_value), current_gc_range));
|
get_pair(new_value), gc_current_range));
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -360,9 +262,12 @@ void collect_garbage(size_t min_free)
|
||||||
else
|
else
|
||||||
collecting = true;
|
collecting = true;
|
||||||
|
|
||||||
|
++gc_counter;
|
||||||
|
gc_ticks -= (int)clock();
|
||||||
|
|
||||||
/* Swap ranges; new "current" range is initially empty, old one is full */
|
/* Swap ranges; new "current" range is initially empty, old one is full */
|
||||||
current_gc_range = 1 - current_gc_range;
|
gc_current_range = 1 - gc_current_range;
|
||||||
gc_free_ptr = (char*)&gc_ranges[current_gc_range][0];
|
gc_free_ptr = (char*)&gc_ranges[gc_current_range][0];
|
||||||
gc_free_space = gc_soft_limit;
|
gc_free_space = gc_soft_limit;
|
||||||
object_ptr = gc_free_ptr;
|
object_ptr = gc_free_ptr;
|
||||||
|
|
||||||
|
|
@ -388,12 +293,12 @@ void collect_garbage(size_t min_free)
|
||||||
size_t min_limit = bytes_used + min_free;
|
size_t min_limit = bytes_used + min_free;
|
||||||
size_t new_limit = (3 * min_limit) / 2;
|
size_t new_limit = (3 * min_limit) / 2;
|
||||||
|
|
||||||
if (new_limit > GC_RANGE_SIZE)
|
if (new_limit > gc_max_size)
|
||||||
new_limit = GC_RANGE_SIZE;
|
new_limit = gc_max_size;
|
||||||
else if (new_limit < GC_INIT_SIZE)
|
else if (new_limit < gc_min_size)
|
||||||
new_limit = GC_INIT_SIZE;
|
new_limit = gc_min_size;
|
||||||
|
|
||||||
gc_free_space += (new_limit - gc_soft_limit);
|
gc_free_space = (gc_free_space + new_limit) - gc_soft_limit;
|
||||||
gc_soft_limit = new_limit;
|
gc_soft_limit = new_limit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -404,57 +309,8 @@ void collect_garbage(size_t min_free)
|
||||||
|
|
||||||
/* Done collecting. */
|
/* Done collecting. */
|
||||||
collecting = false;
|
collecting = false;
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
gc_ticks += (int)clock();
|
||||||
{
|
|
||||||
struct timeval start_time;
|
|
||||||
gc_root_t list_root;
|
|
||||||
int count = 0;
|
|
||||||
|
|
||||||
gettimeofday(&start_time, NULL);
|
|
||||||
srand((unsigned int)start_time.tv_usec);
|
|
||||||
|
|
||||||
register_gc_root(&list_root, NIL);
|
|
||||||
|
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
int r = rand() & 0xffff;
|
|
||||||
|
|
||||||
if (r == 0)
|
|
||||||
list_root.value = to_fixnum(rand());
|
|
||||||
else if (r & 1)
|
|
||||||
list_root.value = cons(to_fixnum(rand()), list_root.value);
|
|
||||||
else if (r & 2)
|
|
||||||
list_root.value = cons(list_root.value, to_fixnum(rand()));
|
|
||||||
else if (r & 4)
|
|
||||||
{
|
|
||||||
list_root.value = cons(list_root.value, cons(to_fixnum(-1), NIL));
|
|
||||||
get_pair(get_pair(list_root.value)->cdr)->cdr = list_root.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cons_counter >= 1000000)
|
|
||||||
{
|
|
||||||
struct timeval end_time;
|
|
||||||
double seconds;
|
|
||||||
gettimeofday(&end_time, NULL);
|
|
||||||
|
|
||||||
seconds = (end_time.tv_sec - start_time.tv_sec)
|
|
||||||
+ 0.000001 * (end_time.tv_usec - start_time.tv_usec);
|
|
||||||
|
|
||||||
printf("%d conses took %.6f seconds.\n", cons_counter, seconds);
|
|
||||||
|
|
||||||
gettimeofday(&start_time, NULL);
|
|
||||||
cons_counter = 0;
|
|
||||||
|
|
||||||
if (++count == 50)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unregister_gc_root(&list_root);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* vim:set sw=2 expandtab: */
|
/* vim:set sw=2 expandtab: */
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,126 @@
|
||||||
|
#ifndef GC_H_6b8a27c99f2eb5eb5437e045fa4af1c3
|
||||||
|
#define GC_H_6b8a27c99f2eb5eb5437e045fa4af1c3
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
# define debug(printf_args) ((void)printf printf_args)
|
||||||
|
#else
|
||||||
|
# define debug(printf_args) ((void)0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef uintptr_t value_t;
|
||||||
|
|
||||||
|
/* NIL: 00000000 00000000 00000000 00000000 */
|
||||||
|
/* Object: aaaaaaaa aaaaaaaa aaaaaaaa aaaaaa00 (where aa... >= 1024) */
|
||||||
|
/* Pair: aaaaaaaa aaaaaaaa aaaaaaaa aaaaaa10 */
|
||||||
|
/* Fixnum: snnnnnnn nnnnnnnn nnnnnnnn nnnnnnn1 */
|
||||||
|
|
||||||
|
#define NIL ((value_t)0)
|
||||||
|
|
||||||
|
/* Special values (0 <= n < 1024) */
|
||||||
|
/* These correspond to objects within the first page of memory */
|
||||||
|
#define SPECIAL_VALUE(n) ((value_t)(4*n+2))
|
||||||
|
#define MAX_SPECIAL SPECIAL_VALUE(1023)
|
||||||
|
|
||||||
|
#define BROKEN_HEART SPECIAL_VALUE(0)
|
||||||
|
#define TYPE_TAG_BOX SPECIAL_VALUE(1)
|
||||||
|
#define TYPE_TAG_VECTOR SPECIAL_VALUE(2)
|
||||||
|
#define TYPE_TAG_BYTESTR SPECIAL_VALUE(3)
|
||||||
|
|
||||||
|
typedef struct pair
|
||||||
|
{
|
||||||
|
value_t car;
|
||||||
|
value_t cdr;
|
||||||
|
} pair_t;
|
||||||
|
|
||||||
|
typedef struct object
|
||||||
|
{
|
||||||
|
value_t tag;
|
||||||
|
union {
|
||||||
|
value_t values[0];
|
||||||
|
char bytes[0];
|
||||||
|
} payload;
|
||||||
|
} object_t;
|
||||||
|
|
||||||
|
typedef struct vector
|
||||||
|
{
|
||||||
|
value_t tag;
|
||||||
|
size_t size;
|
||||||
|
value_t elements[0];
|
||||||
|
} vector_t;
|
||||||
|
|
||||||
|
typedef struct byte_string
|
||||||
|
{
|
||||||
|
value_t tag;
|
||||||
|
size_t size;
|
||||||
|
uint8_t bytes[0];
|
||||||
|
} byte_string_t;
|
||||||
|
|
||||||
|
typedef struct gc_root
|
||||||
|
{
|
||||||
|
value_t value;
|
||||||
|
struct gc_root *prev;
|
||||||
|
struct gc_root *next;
|
||||||
|
} gc_root_t;
|
||||||
|
|
||||||
|
static inline bool is_pair(value_t v)
|
||||||
|
{
|
||||||
|
return ((v & 0x3) == 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool is_object(value_t v)
|
||||||
|
{
|
||||||
|
/* Neither pairs nor objects can exist below (void*)4096. */
|
||||||
|
return ((v & 0x1) == 0) && (v > MAX_SPECIAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool is_fixnum(value_t v)
|
||||||
|
{
|
||||||
|
return (v & 1) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline value_t to_fixnum(intptr_t n)
|
||||||
|
{
|
||||||
|
return (value_t)(n << 1) | 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline intptr_t from_fixnum(value_t n)
|
||||||
|
{
|
||||||
|
return ((intptr_t)n) >> 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline value_t pair_value(pair_t *p)
|
||||||
|
{
|
||||||
|
assert((uintptr_t)p >= 4096);
|
||||||
|
assert(((uintptr_t)p & 3) == 0);
|
||||||
|
return (value_t)p + 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline value_t object_value(void *obj)
|
||||||
|
{
|
||||||
|
assert((uintptr_t)obj >= 4096);
|
||||||
|
assert(((uintptr_t)obj & 3) == 0);
|
||||||
|
return (value_t)obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
object_t *get_object(value_t v);
|
||||||
|
pair_t *get_pair(value_t pair);
|
||||||
|
value_t get_cdr(value_t pair);
|
||||||
|
value_t cons(value_t car, value_t cdr);
|
||||||
|
|
||||||
|
void gc_init(size_t min_size, size_t max_size);
|
||||||
|
void register_gc_root(gc_root_t *root, value_t v);
|
||||||
|
void unregister_gc_root(gc_root_t *root);
|
||||||
|
void *gc_alloc(size_t nbytes);
|
||||||
|
void collect_garbage(size_t min_free);
|
||||||
|
|
||||||
|
/* To be provided by the main application */
|
||||||
|
void out_of_memory(void) __attribute__ ((noreturn));
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* vim:set sw=2 expandtab: */
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
#include <sys/time.h>
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include "gc.h"
|
||||||
|
|
||||||
|
int gc_counter;
|
||||||
|
int gc_ticks;
|
||||||
|
|
||||||
|
void out_of_memory(void)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Out of memory!\n");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
gc_root_t list_root;
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
gc_init(1024, 256*1024*1024);
|
||||||
|
|
||||||
|
gc_counter = 0;
|
||||||
|
gc_ticks = 0;
|
||||||
|
|
||||||
|
srand((unsigned int)time(NULL));
|
||||||
|
|
||||||
|
register_gc_root(&list_root, NIL);
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
int r = rand() & 0xffff;
|
||||||
|
|
||||||
|
if (r == 0)
|
||||||
|
list_root.value = to_fixnum(rand());
|
||||||
|
else if (r & 1)
|
||||||
|
list_root.value = cons(to_fixnum(rand()), list_root.value);
|
||||||
|
else if (r & 2)
|
||||||
|
list_root.value = cons(list_root.value, to_fixnum(rand()));
|
||||||
|
else if (r & 4)
|
||||||
|
{
|
||||||
|
list_root.value = cons(list_root.value, cons(to_fixnum(-1), NIL));
|
||||||
|
get_pair(get_pair(list_root.value)->cdr)->cdr = list_root.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gc_counter >= 1000)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%d collections in %0.3f seconds = %0.3f usec / GC\n",
|
||||||
|
gc_counter,
|
||||||
|
gc_ticks / (double)CLOCKS_PER_SEC,
|
||||||
|
(1000000 * (gc_ticks / (double)CLOCKS_PER_SEC)) / gc_counter);
|
||||||
|
gc_counter = gc_ticks = 0;
|
||||||
|
|
||||||
|
if (++count == 10)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unregister_gc_root(&list_root);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* vim:set sw=2 expandtab: */
|
||||||
Loading…
Reference in New Issue