From 4ccb489869268dfd6f05b452f39bbcaa2b5ddb9b Mon Sep 17 00:00:00 2001 From: Andy Wingo Date: Tue, 9 Aug 2022 11:21:02 +0200 Subject: [PATCH] Set fixed heap size, parallelism via explicit options --- bdw.h | 87 +++++++++++++++++++++++++++++++++++++++++++---- gc-api.h | 23 +++++++++++++ mt-gcbench.c | 14 ++++++-- parallel-tracer.h | 11 ++---- quads.c | 14 ++++++-- semi.h | 74 ++++++++++++++++++++++++++++++++++++++-- serial-tracer.h | 2 +- whippet.h | 80 +++++++++++++++++++++++++++++++++++++++---- 8 files changed, 275 insertions(+), 30 deletions(-) diff --git a/bdw.h b/bdw.h index 69a147b56..2d30fb3b6 100644 --- a/bdw.h +++ b/bdw.h @@ -1,4 +1,5 @@ #include +#include #include "conservative-roots.h" @@ -120,17 +121,89 @@ static inline struct heap *mutator_heap(struct mutator *mutator) { return mutator->heap; } -static int initialize_gc(size_t heap_size, struct heap **heap, - struct mutator **mutator) { +#define FOR_EACH_GC_OPTION(M) \ + M(GC_OPTION_FIXED_HEAP_SIZE, "fixed-heap-size") \ + M(GC_OPTION_PARALLELISM, "parallelism") + +static void dump_available_gc_options(void) { + fprintf(stderr, "available gc options:"); +#define PRINT_OPTION(option, name) fprintf(stderr, " %s", name); + FOR_EACH_GC_OPTION(PRINT_OPTION) +#undef PRINT_OPTION + fprintf(stderr, "\n"); +} + +static int gc_option_from_string(const char *str) { +#define PARSE_OPTION(option, name) if (strcmp(str, name) == 0) return option; + FOR_EACH_GC_OPTION(PARSE_OPTION) +#undef PARSE_OPTION + if (strcmp(str, "fixed-heap-size") == 0) + return GC_OPTION_FIXED_HEAP_SIZE; + if (strcmp(str, "parallelism") == 0) + return GC_OPTION_PARALLELISM; + fprintf(stderr, "bad gc option: '%s'\n", str); + dump_available_gc_options(); + return -1; +} + +struct options { + size_t fixed_heap_size; + size_t parallelism; +}; + +static size_t parse_size_t(double value) { + ASSERT(value >= 0); + ASSERT(value <= (size_t) -1); + return value; +} + +static size_t number_of_current_processors(void) { return 1; } + +static int parse_options(int argc, struct gc_option argv[], + struct options *options) { + for (int i = 0; i < argc; i++) { + switch (argv[i].option) { + case GC_OPTION_FIXED_HEAP_SIZE: + options->fixed_heap_size = parse_size_t(argv[i].value); + break; + case GC_OPTION_PARALLELISM: + options->parallelism = parse_size_t(argv[i].value); + break; + default: + abort(); + } + } + + if (!options->fixed_heap_size) { + fprintf(stderr, "fixed heap size is currently required\n"); + return 0; + } + if (!options->parallelism) + options->parallelism = number_of_current_processors(); + + return 1; +} + +static int gc_init(int argc, struct gc_option argv[], + struct heap **heap, struct mutator **mutator) { + struct options options = { 0, }; + if (!parse_options(argc, argv, &options)) + return 0; + // GC_full_freq = 30; // GC_free_space_divisor = 16; // GC_enable_incremental(); - GC_INIT(); + + GC_set_max_heap_size(options.fixed_heap_size); + // Not part of 7.3, sigh. Have to set an env var. + // GC_set_markers_count(options.parallelism); + char markers[21] = {0,}; // 21 bytes enough for 2**64 in decimal + NUL. + snprintf(markers, sizeof(markers), "%zu", options.parallelism); + setenv("GC_MARKERS", markers, 1); + GC_init(); size_t current_heap_size = GC_get_heap_size(); - if (heap_size > current_heap_size) { - GC_set_max_heap_size (heap_size); - GC_expand_hp(heap_size - current_heap_size); - } + if (options.fixed_heap_size > current_heap_size) + GC_expand_hp(options.fixed_heap_size - current_heap_size); GC_allow_register_threads(); *heap = GC_malloc(sizeof(struct heap)); pthread_mutex_init(&(*heap)->lock, NULL); diff --git a/gc-api.h b/gc-api.h index 6c072bc31..7d6eead51 100644 --- a/gc-api.h +++ b/gc-api.h @@ -59,4 +59,27 @@ static inline void gc_edge_update(struct gc_edge edge, struct gc_ref ref) { *edge.dst = ref; } +// FIXME: prefix with gc_ +struct heap; +struct mutator; + +enum { + GC_OPTION_FIXED_HEAP_SIZE, + GC_OPTION_PARALLELISM +}; + +struct gc_option { + int option; + double value; +}; + +// FIXME: Conflict with bdw-gc GC_API. Switch prefix? +#ifndef GC_API_ +#define GC_API_ static +#endif + +GC_API_ int gc_option_from_string(const char *str); +GC_API_ int gc_init(int argc, struct gc_option argv[], + struct heap **heap, struct mutator **mutator); + #endif // GC_API_H_ diff --git a/mt-gcbench.c b/mt-gcbench.c index 4d1c92255..23fb235c2 100644 --- a/mt-gcbench.c +++ b/mt-gcbench.c @@ -380,13 +380,14 @@ int main(int argc, char *argv[]) { tree_size(long_lived_tree_depth) * sizeof_node + tree_size(max_tree_depth) * sizeof_node + sizeof_double_array + sizeof(double) * array_size; - if (argc != 3) { - fprintf(stderr, "usage: %s MULTIPLIER NTHREADS\n", argv[0]); + if (argc != 4) { + fprintf(stderr, "usage: %s MULTIPLIER NTHREADS PARALLELISM\n", argv[0]); return 1; } double multiplier = atof(argv[1]); size_t nthreads = atol(argv[2]); + size_t parallelism = atol(argv[3]); if (!(0.1 < multiplier && multiplier < 100)) { fprintf(stderr, "Failed to parse heap multiplier '%s'\n", argv[1]); @@ -397,11 +398,18 @@ int main(int argc, char *argv[]) { (int)MAX_THREAD_COUNT, argv[2]); return 1; } + if (parallelism < 1 || parallelism > MAX_THREAD_COUNT) { + fprintf(stderr, "Expected integer between 1 and %d for parallelism, got '%s'\n", + (int)MAX_THREAD_COUNT, argv[3]); + return 1; + } size_t heap_size = heap_max_live * multiplier * nthreads; + struct gc_option options[] = { { GC_OPTION_FIXED_HEAP_SIZE, heap_size }, + { GC_OPTION_PARALLELISM, parallelism } }; struct heap *heap; struct mutator *mut; - if (!initialize_gc(heap_size, &heap, &mut)) { + if (!gc_init(sizeof options / sizeof options[0], options, &heap, &mut)) { fprintf(stderr, "Failed to initialize GC with heap size %zu bytes\n", heap_size); return 1; diff --git a/parallel-tracer.h b/parallel-tracer.h index 92c0a86d4..0634c91ec 100644 --- a/parallel-tracer.h +++ b/parallel-tracer.h @@ -325,8 +325,6 @@ struct local_tracer { struct context; static inline struct tracer* heap_tracer(struct heap *heap); -static size_t number_of_current_processors(void) { return 1; } - static int trace_worker_init(struct trace_worker *worker, struct heap *heap, struct tracer *tracer, size_t id) { @@ -416,18 +414,15 @@ trace_worker_request_stop(struct trace_worker *worker) { } static int -tracer_init(struct heap *heap) { +tracer_init(struct heap *heap, size_t parallelism) { struct tracer *tracer = heap_tracer(heap); atomic_init(&tracer->active_tracers, 0); atomic_init(&tracer->running_tracers, 0); tracer->count = 0; pthread_mutex_init(&tracer->lock, NULL); pthread_cond_init(&tracer->cond, NULL); - size_t desired_worker_count = 0; - if (getenv("GC_TRACERS")) - desired_worker_count = atoi(getenv("GC_TRACERS")); - if (desired_worker_count == 0) - desired_worker_count = number_of_current_processors(); + size_t desired_worker_count = parallelism; + ASSERT(desired_worker_count); if (desired_worker_count > TRACE_WORKERS_MAX_COUNT) desired_worker_count = TRACE_WORKERS_MAX_COUNT; for (size_t i = 0; i < desired_worker_count; i++) { diff --git a/quads.c b/quads.c index d2cba2bf0..9c6dedfaf 100644 --- a/quads.c +++ b/quads.c @@ -103,20 +103,27 @@ static size_t tree_size(size_t depth) { return nquads; } +#define MAX_THREAD_COUNT 256 int main(int argc, char *argv[]) { if (argc != 3) { - fprintf(stderr, "usage: %s DEPTH MULTIPLIER\n", argv[0]); + fprintf(stderr, "usage: %s DEPTH MULTIPLIER PARALLELISM\n", argv[0]); return 1; } size_t depth = parse_size(argv[1], "depth"); double multiplier = atof(argv[2]); + size_t parallelism = atol(argv[3]); if (!(1.0 < multiplier && multiplier < 100)) { fprintf(stderr, "Failed to parse heap multiplier '%s'\n", argv[2]); return 1; } + if (parallelism < 1 || parallelism > MAX_THREAD_COUNT) { + fprintf(stderr, "Expected integer between 1 and %d for parallelism, got '%s'\n", + (int)MAX_THREAD_COUNT, argv[3]); + return 1; + } // Compute byte size not counting any header word, so as to compute the same // heap size whether a header word is there or not. @@ -127,9 +134,12 @@ int main(int argc, char *argv[]) { unsigned long gc_start = current_time(); printf("Allocating heap of %.3fGB (%.2f multiplier of live data).\n", heap_size / 1e9, multiplier); + + struct gc_option options[] = { { GC_OPTION_FIXED_HEAP_SIZE, heap_size }, + { GC_OPTION_PARALLELISM, parallelism } }; struct heap *heap; struct mutator *mut; - if (!initialize_gc(heap_size, &heap, &mut)) { + if (!gc_init(sizeof options / sizeof options[0], options, &heap, &mut)) { fprintf(stderr, "Failed to initialize GC with heap size %zu bytes\n", heap_size); return 1; diff --git a/semi.h b/semi.h index 7cd776398..0f6071266 100644 --- a/semi.h +++ b/semi.h @@ -285,14 +285,82 @@ static int initialize_semi_space(struct semi_space *space, size_t size) { return 1; } -static int initialize_gc(size_t heap_size, struct heap **heap, - struct mutator **mut) { +#define FOR_EACH_GC_OPTION(M) \ + M(GC_OPTION_FIXED_HEAP_SIZE, "fixed-heap-size") \ + M(GC_OPTION_PARALLELISM, "parallelism") + +static void dump_available_gc_options(void) { + fprintf(stderr, "available gc options:"); +#define PRINT_OPTION(option, name) fprintf(stderr, " %s", name); + FOR_EACH_GC_OPTION(PRINT_OPTION) +#undef PRINT_OPTION + fprintf(stderr, "\n"); +} + +static int gc_option_from_string(const char *str) { +#define PARSE_OPTION(option, name) if (strcmp(str, name) == 0) return option; + FOR_EACH_GC_OPTION(PARSE_OPTION) +#undef PARSE_OPTION + if (strcmp(str, "fixed-heap-size") == 0) + return GC_OPTION_FIXED_HEAP_SIZE; + if (strcmp(str, "parallelism") == 0) + return GC_OPTION_PARALLELISM; + fprintf(stderr, "bad gc option: '%s'\n", str); + dump_available_gc_options(); + return -1; +} + +struct options { + size_t fixed_heap_size; + size_t parallelism; +}; + +static size_t parse_size_t(double value) { + ASSERT(value >= 0); + ASSERT(value <= (size_t) -1); + return value; +} + +static int parse_options(int argc, struct gc_option argv[], + struct options *options) { + options->parallelism = 1; + for (int i = 0; i < argc; i++) { + switch (argv[i].option) { + case GC_OPTION_FIXED_HEAP_SIZE: + options->fixed_heap_size = parse_size_t(argv[i].value); + break; + case GC_OPTION_PARALLELISM: + options->parallelism = parse_size_t(argv[i].value); + break; + default: + abort(); + } + } + + if (!options->fixed_heap_size) { + fprintf(stderr, "fixed heap size is currently required\n"); + return 0; + } + if (options->parallelism != 1) { + fprintf(stderr, "parallelism unimplemented in semispace copying collector\n"); + return 0; + } + + return 1; +} + +static int gc_init(int argc, struct gc_option argv[], + struct heap **heap, struct mutator **mut) { + struct options options = { 0, }; + if (!parse_options(argc, argv, &options)) + return 0; + *mut = calloc(1, sizeof(struct mutator)); if (!*mut) abort(); *heap = mutator_heap(*mut); struct semi_space *space = mutator_semi_space(*mut); - if (!initialize_semi_space(space, heap_size)) + if (!initialize_semi_space(space, options.fixed_heap_size)) return 0; if (!large_object_space_init(heap_large_object_space(*heap), *heap)) return 0; diff --git a/serial-tracer.h b/serial-tracer.h index 3376d9608..474de34d7 100644 --- a/serial-tracer.h +++ b/serial-tracer.h @@ -129,7 +129,7 @@ struct heap; static inline struct tracer* heap_tracer(struct heap *heap); static int -tracer_init(struct heap *heap) { +tracer_init(struct heap *heap, size_t parallelism) { return trace_queue_init(&heap_tracer(heap)->queue); } static void tracer_prepare(struct heap *heap) {} diff --git a/whippet.h b/whippet.h index 93936aaae..b100ca04f 100644 --- a/whippet.h +++ b/whippet.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include "assert.h" @@ -1910,6 +1911,69 @@ static inline void set_field(void *obj, void **addr, void *val) { *addr = val; } +#define FOR_EACH_GC_OPTION(M) \ + M(GC_OPTION_FIXED_HEAP_SIZE, "fixed-heap-size") \ + M(GC_OPTION_PARALLELISM, "parallelism") + +static void dump_available_gc_options(void) { + fprintf(stderr, "available gc options:"); +#define PRINT_OPTION(option, name) fprintf(stderr, " %s", name); + FOR_EACH_GC_OPTION(PRINT_OPTION) +#undef PRINT_OPTION + fprintf(stderr, "\n"); +} + +static int gc_option_from_string(const char *str) { +#define PARSE_OPTION(option, name) if (strcmp(str, name) == 0) return option; + FOR_EACH_GC_OPTION(PARSE_OPTION) +#undef PARSE_OPTION + if (strcmp(str, "fixed-heap-size") == 0) + return GC_OPTION_FIXED_HEAP_SIZE; + if (strcmp(str, "parallelism") == 0) + return GC_OPTION_PARALLELISM; + fprintf(stderr, "bad gc option: '%s'\n", str); + dump_available_gc_options(); + return -1; +} + +struct options { + size_t fixed_heap_size; + size_t parallelism; +}; + +static size_t parse_size_t(double value) { + ASSERT(value >= 0); + ASSERT(value <= (size_t) -1); + return value; +} + +static size_t number_of_current_processors(void) { return 1; } + +static int parse_options(int argc, struct gc_option argv[], + struct options *options) { + for (int i = 0; i < argc; i++) { + switch (argv[i].option) { + case GC_OPTION_FIXED_HEAP_SIZE: + options->fixed_heap_size = parse_size_t(argv[i].value); + break; + case GC_OPTION_PARALLELISM: + options->parallelism = parse_size_t(argv[i].value); + break; + default: + abort(); + } + } + + if (!options->fixed_heap_size) { + fprintf(stderr, "fixed heap size is currently required\n"); + return 0; + } + if (!options->parallelism) + options->parallelism = number_of_current_processors(); + + return 1; +} + static struct slab* allocate_slabs(size_t nslabs) { size_t size = nslabs * SLAB_SIZE; size_t extent = size + SLAB_SIZE; @@ -1934,15 +1998,15 @@ static struct slab* allocate_slabs(size_t nslabs) { return (struct slab*) aligned_base; } -static int heap_init(struct heap *heap, size_t size) { +static int heap_init(struct heap *heap, struct options *options) { // *heap is already initialized to 0. pthread_mutex_init(&heap->lock, NULL); pthread_cond_init(&heap->mutator_cond, NULL); pthread_cond_init(&heap->collector_cond, NULL); - heap->size = size; + heap->size = options->fixed_heap_size; - if (!tracer_init(heap)) + if (!tracer_init(heap, options->parallelism)) abort(); heap->fragmentation_low_threshold = 0.05; @@ -1987,12 +2051,16 @@ static int mark_space_init(struct mark_space *space, struct heap *heap) { return 1; } -static int initialize_gc(size_t size, struct heap **heap, - struct mutator **mut) { +static int gc_init(int argc, struct gc_option argv[], + struct heap **heap, struct mutator **mut) { + struct options options = { 0, }; + if (!parse_options(argc, argv, &options)) + return 0; + *heap = calloc(1, sizeof(struct heap)); if (!*heap) abort(); - if (!heap_init(*heap, size)) + if (!heap_init(*heap, &options)) abort(); struct mark_space *space = heap_mark_space(*heap);