summaryrefslogtreecommitdiff
path: root/gc.c
diff options
context:
space:
mode:
authorPeter Zhu <peter@peterzhu.ca>2021-11-19 14:51:58 -0500
committerPeter Zhu <peter@peterzhu.ca>2021-11-23 10:51:27 -0500
commit9aded89f4071a8afb79326701789241f1da12f82 (patch)
tree4869f264a95718d7c6882564cf0d19a913fe952e /gc.c
parentc14f230b26aa4f8abe9ecf3814cfebbe584d77c9 (diff)
Speed up Ractors for Variable Width Allocation
This commit adds a Ractor cache for every size pool. Previously, all VWA allocated objects used the slowpath and locked the VM. On a micro-benchmark that benchmarks String allocation: VWA turned off: 29.196591 0.889709 30.086300 ( 9.434059) VWA before this commit: 29.279486 41.477869 70.757355 ( 12.527379) VWA after this commit: 16.782903 0.557117 17.340020 ( 4.255603)
Notes
Notes: Merged: https://github.com/ruby/ruby/pull/5151
Diffstat (limited to 'gc.c')
-rw-r--r--gc.c152
1 files changed, 59 insertions, 93 deletions
diff --git a/gc.c b/gc.c
index b2c5ca0a80..e0ba25860c 100644
--- a/gc.c
+++ b/gc.c
@@ -659,11 +659,6 @@ typedef struct mark_stack {
size_t unused_cache_size;
} mark_stack_t;
-#if USE_RVARGC
-#define SIZE_POOL_COUNT 4
-#else
-#define SIZE_POOL_COUNT 1
-#endif
#define SIZE_POOL_EDEN_HEAP(size_pool) (&(size_pool)->eden_heap)
#define SIZE_POOL_TOMB_HEAP(size_pool) (&(size_pool)->tomb_heap)
@@ -681,11 +676,6 @@ typedef struct rb_heap_struct {
} rb_heap_t;
typedef struct rb_size_pool_struct {
-#if USE_RVARGC
- RVALUE *freelist;
- struct heap_page *using_page;
-#endif
-
short slot_size;
size_t allocatable_pages;
@@ -2325,7 +2315,7 @@ newobj_init(VALUE klass, VALUE flags, int wb_protected, rb_objspace_t *objspace,
static inline void heap_add_freepage(rb_heap_t *heap, struct heap_page *page);
static struct heap_page *heap_next_freepage(rb_objspace_t *objspace, rb_size_pool_t *size_pool, rb_heap_t *heap);
-static inline void ractor_set_cache(rb_ractor_t *cr, struct heap_page *page);
+static inline void ractor_set_cache(rb_ractor_t *cr, struct heap_page *page, size_t size_pool_idx);
size_t
rb_gc_obj_slot_size(VALUE obj)
@@ -2355,17 +2345,14 @@ rb_gc_size_allocatable_p(size_t size)
}
static inline VALUE
-ractor_cached_free_region(rb_objspace_t *objspace, rb_ractor_t *cr, size_t size)
+ractor_cached_free_region(rb_objspace_t *objspace, rb_ractor_t *cr, size_t size_pool_idx)
{
- if (size > sizeof(RVALUE)) {
- return Qfalse;
- }
-
- RVALUE *p = cr->newobj_cache.freelist;
+ rb_ractor_newobj_size_pool_cache_t *cache = &cr->newobj_cache.size_pool_caches[size_pool_idx];
+ RVALUE *p = cache->freelist;
if (p) {
VALUE obj = (VALUE)p;
- cr->newobj_cache.freelist = p->as.free.next;
+ cache->freelist = p->as.free.next;
asan_unpoison_object(obj, true);
return obj;
}
@@ -2396,28 +2383,31 @@ heap_next_freepage(rb_objspace_t *objspace, rb_size_pool_t *size_pool, rb_heap_t
}
static inline void
-ractor_set_cache(rb_ractor_t *cr, struct heap_page *page)
+ractor_set_cache(rb_ractor_t *cr, struct heap_page *page, size_t size_pool_idx)
{
gc_report(3, &rb_objspace, "ractor_set_cache: Using page %p\n", (void *)GET_PAGE_BODY(page->start));
- cr->newobj_cache.using_page = page;
- cr->newobj_cache.freelist = page->freelist;
+
+ rb_ractor_newobj_size_pool_cache_t *cache = &cr->newobj_cache.size_pool_caches[size_pool_idx];
+
+ cache->using_page = page;
+ cache->freelist = page->freelist;
page->free_slots = 0;
page->freelist = NULL;
- asan_unpoison_object((VALUE)cr->newobj_cache.freelist, false);
- GC_ASSERT(RB_TYPE_P((VALUE)cr->newobj_cache.freelist, T_NONE));
- asan_poison_object((VALUE)cr->newobj_cache.freelist);
+ asan_unpoison_object((VALUE)cache->freelist, false);
+ GC_ASSERT(RB_TYPE_P((VALUE)cache->freelist, T_NONE));
+ asan_poison_object((VALUE)cache->freelist);
}
static inline void
-ractor_cache_slots(rb_objspace_t *objspace, rb_ractor_t *cr)
+ractor_cache_slots(rb_objspace_t *objspace, rb_ractor_t *cr, size_t size_pool_idx)
{
ASSERT_vm_locking();
- rb_size_pool_t *size_pool = &size_pools[0];
+ rb_size_pool_t *size_pool = &size_pools[size_pool_idx];
struct heap_page *page = heap_next_freepage(objspace, size_pool, SIZE_POOL_EDEN_HEAP(size_pool));
- ractor_set_cache(cr, page);
+ ractor_set_cache(cr, page, size_pool_idx);
}
static inline VALUE
@@ -2430,10 +2420,10 @@ newobj_fill(VALUE obj, VALUE v1, VALUE v2, VALUE v3)
return obj;
}
-#if USE_RVARGC
-static inline rb_size_pool_t *
-size_pool_for_size(rb_objspace_t *objspace, size_t size)
+static inline size_t
+size_pool_idx_for_size(size_t size)
{
+#if USE_RVARGC
size_t slot_count = CEILDIV(size, sizeof(RVALUE));
/* size_pool_idx is ceil(log2(slot_count)) */
@@ -2442,41 +2432,31 @@ size_pool_for_size(rb_objspace_t *objspace, size_t size)
rb_bug("size_pool_for_size: allocation size too large");
}
- rb_size_pool_t *size_pool = &size_pools[size_pool_idx];
- GC_ASSERT(size_pool->slot_size >= (short)size);
- GC_ASSERT(size_pool_idx == 0 || size_pools[size_pool_idx - 1].slot_size < (short)size);
-
- return size_pool;
+ return size_pool_idx;
+#else
+ GC_ASSERT(size <= sizeof(RVALUE));
+ return 0;
+#endif
}
-
-static inline VALUE
-heap_get_freeobj(rb_objspace_t *objspace, rb_size_pool_t *size_pool, rb_heap_t *heap)
+#if USE_RVARGC
+static inline rb_size_pool_t *
+size_pool_for_size(rb_objspace_t *objspace, size_t size)
{
- RVALUE *p = size_pool->freelist;
-
- if (UNLIKELY(p == NULL)) {
- struct heap_page *page = heap_next_freepage(objspace, size_pool, heap);
- size_pool->using_page = page;
-
- asan_unpoison_memory_region(&page->freelist, sizeof(RVALUE*), false);
- p = page->freelist;
- page->freelist = NULL;
- asan_poison_memory_region(&page->freelist, sizeof(RVALUE*));
- page->free_slots = 0;
- }
+ size_t size_pool_idx = size_pool_idx_for_size(size);
- asan_unpoison_object((VALUE)p, true);
- size_pool->freelist = p->as.free.next;
+ rb_size_pool_t *size_pool = &size_pools[size_pool_idx];
+ GC_ASSERT(size_pool->slot_size >= (short)size);
+ GC_ASSERT(size_pool_idx == 0 || size_pools[size_pool_idx - 1].slot_size < (short)size);
- return (VALUE)p;
+ return size_pool;
}
#endif
-ALWAYS_INLINE(static VALUE newobj_slowpath(VALUE klass, VALUE flags, rb_objspace_t *objspace, rb_ractor_t *cr, int wb_protected, size_t alloc_size));
+ALWAYS_INLINE(static VALUE newobj_slowpath(VALUE klass, VALUE flags, rb_objspace_t *objspace, rb_ractor_t *cr, int wb_protected, size_t size_pool_idx));
static inline VALUE
-newobj_slowpath(VALUE klass, VALUE flags, rb_objspace_t *objspace, rb_ractor_t *cr, int wb_protected, size_t alloc_size)
+newobj_slowpath(VALUE klass, VALUE flags, rb_objspace_t *objspace, rb_ractor_t *cr, int wb_protected, size_t size_pool_idx)
{
VALUE obj;
unsigned int lev;
@@ -2497,22 +2477,9 @@ newobj_slowpath(VALUE klass, VALUE flags, rb_objspace_t *objspace, rb_ractor_t *
}
}
- if (alloc_size <= sizeof(RVALUE)) {
- // allocate new slot
- while ((obj = ractor_cached_free_region(objspace, cr, alloc_size)) == Qfalse) {
- ractor_cache_slots(objspace, cr);
- }
- }
- else {
-#if USE_RVARGC
- rb_size_pool_t *size_pool = size_pool_for_size(objspace, alloc_size);
-
- obj = heap_get_freeobj(objspace, size_pool, SIZE_POOL_EDEN_HEAP(size_pool));
-
- memset((void *)obj, 0, size_pool->slot_size);
-#else
- rb_bug("unreachable when not using rvargc");
-#endif
+ // allocate new slot
+ while ((obj = ractor_cached_free_region(objspace, cr, size_pool_idx)) == Qfalse) {
+ ractor_cache_slots(objspace, cr, size_pool_idx);
}
GC_ASSERT(obj != 0);
newobj_init(klass, flags, wb_protected, objspace, obj);
@@ -2525,20 +2492,20 @@ newobj_slowpath(VALUE klass, VALUE flags, rb_objspace_t *objspace, rb_ractor_t *
}
NOINLINE(static VALUE newobj_slowpath_wb_protected(VALUE klass, VALUE flags,
- rb_objspace_t *objspace, rb_ractor_t *cr, size_t alloc_size));
+ rb_objspace_t *objspace, rb_ractor_t *cr, size_t size_pool_idx));
NOINLINE(static VALUE newobj_slowpath_wb_unprotected(VALUE klass, VALUE flags,
- rb_objspace_t *objspace, rb_ractor_t *cr, size_t alloc_size));
+ rb_objspace_t *objspace, rb_ractor_t *cr, size_t size_pool_idx));
static VALUE
-newobj_slowpath_wb_protected(VALUE klass, VALUE flags, rb_objspace_t *objspace, rb_ractor_t *cr, size_t alloc_size)
+newobj_slowpath_wb_protected(VALUE klass, VALUE flags, rb_objspace_t *objspace, rb_ractor_t *cr, size_t size_pool_idx)
{
- return newobj_slowpath(klass, flags, objspace, cr, TRUE, alloc_size);
+ return newobj_slowpath(klass, flags, objspace, cr, TRUE, size_pool_idx);
}
static VALUE
-newobj_slowpath_wb_unprotected(VALUE klass, VALUE flags, rb_objspace_t *objspace, rb_ractor_t *cr, size_t alloc_size)
+newobj_slowpath_wb_unprotected(VALUE klass, VALUE flags, rb_objspace_t *objspace, rb_ractor_t *cr, size_t size_pool_idx)
{
- return newobj_slowpath(klass, flags, objspace, cr, FALSE, alloc_size);
+ return newobj_slowpath(klass, flags, objspace, cr, FALSE, size_pool_idx);
}
static inline VALUE
@@ -2559,11 +2526,13 @@ newobj_of0(VALUE klass, VALUE flags, int wb_protected, rb_ractor_t *cr, size_t a
}
#endif
+ size_t size_pool_idx = size_pool_idx_for_size(alloc_size);
+
if ((!UNLIKELY(during_gc ||
ruby_gc_stressful ||
gc_event_hook_available_p(objspace)) &&
wb_protected &&
- (obj = ractor_cached_free_region(objspace, cr, alloc_size)) != Qfalse)) {
+ (obj = ractor_cached_free_region(objspace, cr, size_pool_idx)) != Qfalse)) {
newobj_init(klass, flags, wb_protected, objspace, obj);
}
@@ -2571,8 +2540,8 @@ newobj_of0(VALUE klass, VALUE flags, int wb_protected, rb_ractor_t *cr, size_t a
RB_DEBUG_COUNTER_INC(obj_newobj_slowpath);
obj = wb_protected ?
- newobj_slowpath_wb_protected(klass, flags, objspace, cr, alloc_size) :
- newobj_slowpath_wb_unprotected(klass, flags, objspace, cr, alloc_size);
+ newobj_slowpath_wb_protected(klass, flags, objspace, cr, size_pool_idx) :
+ newobj_slowpath_wb_unprotected(klass, flags, objspace, cr, size_pool_idx);
}
return obj;
@@ -5585,13 +5554,6 @@ gc_sweep_start(rb_objspace_t *objspace)
for (int i = 0; i < SIZE_POOL_COUNT; i++) {
rb_size_pool_t *size_pool = &size_pools[i];
-#if USE_RVARGC
- heap_page_freelist_append(size_pool->using_page, size_pool->freelist);
-
- size_pool->using_page = NULL;
- size_pool->freelist = NULL;
-#endif
-
gc_sweep_start_heap(objspace, SIZE_POOL_EDEN_HEAP(size_pool));
}
@@ -8698,14 +8660,18 @@ rb_obj_gc_flags(VALUE obj, ID* flags, size_t max)
void
rb_gc_ractor_newobj_cache_clear(rb_ractor_newobj_cache_t *newobj_cache)
{
- struct heap_page *page = newobj_cache->using_page;
- RVALUE *freelist = newobj_cache->freelist;
- RUBY_DEBUG_LOG("ractor using_page:%p freelist:%p", (void *)page, (void *)freelist);
+ for (size_t size_pool_idx = 0; size_pool_idx < SIZE_POOL_COUNT; size_pool_idx++) {
+ rb_ractor_newobj_size_pool_cache_t *cache = &newobj_cache->size_pool_caches[size_pool_idx];
+
+ struct heap_page *page = cache->using_page;
+ RVALUE *freelist = cache->freelist;
+ RUBY_DEBUG_LOG("ractor using_page:%p freelist:%p", (void *)page, (void *)freelist);
- heap_page_freelist_append(page, freelist);
+ heap_page_freelist_append(page, freelist);
- newobj_cache->using_page = NULL;
- newobj_cache->freelist = NULL;
+ cache->using_page = NULL;
+ cache->freelist = NULL;
+ }
}
void