зеркало из https://github.com/github/ruby.git
cancel theap on multi-ractors
accessing theap needs complicating synchronization but it reduce performance on multi-ractor mode. So simply stop using theap on multi-ractor mode. In future, theap should be replaced with more cleaver memory strategy.
This commit is contained in:
Родитель
b67b24d0f5
Коммит
307732ccee
|
@ -10384,6 +10384,7 @@ ractor.$(OBJEXT): {$(VPATH)}subst.h
|
||||||
ractor.$(OBJEXT): {$(VPATH)}thread.h
|
ractor.$(OBJEXT): {$(VPATH)}thread.h
|
||||||
ractor.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
|
ractor.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
|
||||||
ractor.$(OBJEXT): {$(VPATH)}thread_native.h
|
ractor.$(OBJEXT): {$(VPATH)}thread_native.h
|
||||||
|
ractor.$(OBJEXT): {$(VPATH)}transient_heap.h
|
||||||
ractor.$(OBJEXT): {$(VPATH)}variable.h
|
ractor.$(OBJEXT): {$(VPATH)}variable.h
|
||||||
ractor.$(OBJEXT): {$(VPATH)}vm_core.h
|
ractor.$(OBJEXT): {$(VPATH)}vm_core.h
|
||||||
ractor.$(OBJEXT): {$(VPATH)}vm_debug.h
|
ractor.$(OBJEXT): {$(VPATH)}vm_debug.h
|
||||||
|
|
39
ractor.c
39
ractor.c
|
@ -14,6 +14,7 @@
|
||||||
#include "internal/struct.h"
|
#include "internal/struct.h"
|
||||||
#include "variable.h"
|
#include "variable.h"
|
||||||
#include "gc.h"
|
#include "gc.h"
|
||||||
|
#include "transient_heap.h"
|
||||||
|
|
||||||
VALUE rb_cRactor;
|
VALUE rb_cRactor;
|
||||||
static VALUE rb_eRactorError;
|
static VALUE rb_eRactorError;
|
||||||
|
@ -1127,15 +1128,32 @@ ractor_next_id(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
vm_insert_ractor0(rb_vm_t *vm, rb_ractor_t *r)
|
vm_insert_ractor0(rb_vm_t *vm, rb_ractor_t *r, bool single_ractor_mode)
|
||||||
{
|
{
|
||||||
RUBY_DEBUG_LOG("r:%u ractor.cnt:%u++", r->id, vm->ractor.cnt);
|
RUBY_DEBUG_LOG("r:%u ractor.cnt:%u++", r->id, vm->ractor.cnt);
|
||||||
VM_ASSERT(!rb_multi_ractor_p() || RB_VM_LOCKED_P());
|
VM_ASSERT(single_ractor_mode || RB_VM_LOCKED_P());
|
||||||
|
|
||||||
list_add_tail(&vm->ractor.set, &r->vmlr_node);
|
list_add_tail(&vm->ractor.set, &r->vmlr_node);
|
||||||
vm->ractor.cnt++;
|
vm->ractor.cnt++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
cancel_single_ractor_mode(void)
|
||||||
|
{
|
||||||
|
// enable multi-ractor mode
|
||||||
|
RUBY_DEBUG_LOG("enable multi-ractor mode", 0);
|
||||||
|
|
||||||
|
rb_gc_start();
|
||||||
|
rb_transient_heap_evacuate();
|
||||||
|
|
||||||
|
if (rb_warning_category_enabled_p(RB_WARN_CATEGORY_EXPERIMENTAL)) {
|
||||||
|
rb_warn("Ractor is experimental, and the behavior may change in future versions of Ruby! "
|
||||||
|
"Also there are many implementation issues.");
|
||||||
|
}
|
||||||
|
|
||||||
|
ruby_single_main_ractor = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
vm_insert_ractor(rb_vm_t *vm, rb_ractor_t *r)
|
vm_insert_ractor(rb_vm_t *vm, rb_ractor_t *r)
|
||||||
{
|
{
|
||||||
|
@ -1144,29 +1162,22 @@ vm_insert_ractor(rb_vm_t *vm, rb_ractor_t *r)
|
||||||
if (rb_multi_ractor_p()) {
|
if (rb_multi_ractor_p()) {
|
||||||
RB_VM_LOCK();
|
RB_VM_LOCK();
|
||||||
{
|
{
|
||||||
vm_insert_ractor0(vm, r);
|
vm_insert_ractor0(vm, r, false);
|
||||||
vm_ractor_blocking_cnt_inc(vm, r, __FILE__, __LINE__);
|
vm_ractor_blocking_cnt_inc(vm, r, __FILE__, __LINE__);
|
||||||
}
|
}
|
||||||
RB_VM_UNLOCK();
|
RB_VM_UNLOCK();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
vm_insert_ractor0(vm, r);
|
if (vm->ractor.cnt == 0) {
|
||||||
|
|
||||||
if (vm->ractor.cnt == 1) {
|
|
||||||
// main ractor
|
// main ractor
|
||||||
|
vm_insert_ractor0(vm, r, true);
|
||||||
ractor_status_set(r, ractor_blocking);
|
ractor_status_set(r, ractor_blocking);
|
||||||
ractor_status_set(r, ractor_running);
|
ractor_status_set(r, ractor_running);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
cancel_single_ractor_mode();
|
||||||
|
vm_insert_ractor0(vm, r, true);
|
||||||
vm_ractor_blocking_cnt_inc(vm, r, __FILE__, __LINE__);
|
vm_ractor_blocking_cnt_inc(vm, r, __FILE__, __LINE__);
|
||||||
|
|
||||||
// enable multi-ractor mode
|
|
||||||
RUBY_DEBUG_LOG("enable multi-ractor mode", 0);
|
|
||||||
ruby_single_main_ractor = NULL;
|
|
||||||
|
|
||||||
if (rb_warning_category_enabled_p(RB_WARN_CATEGORY_EXPERIMENTAL)) {
|
|
||||||
rb_warn("Ractor is experimental, and the behavior may change in future versions of Ruby! Also there are many implementation issues.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
136
transient_heap.c
136
transient_heap.c
|
@ -364,74 +364,72 @@ transient_heap_allocatable_header(struct transient_heap* theap, size_t size)
|
||||||
void *
|
void *
|
||||||
rb_transient_heap_alloc(VALUE obj, size_t req_size)
|
rb_transient_heap_alloc(VALUE obj, size_t req_size)
|
||||||
{
|
{
|
||||||
|
// only on single main ractor
|
||||||
|
if (ruby_single_main_ractor == NULL) return NULL;
|
||||||
|
|
||||||
void *ret;
|
void *ret;
|
||||||
|
struct transient_heap* theap = transient_heap_get();
|
||||||
|
size_t size = ROUND_UP(req_size + sizeof(struct transient_alloc_header), TRANSIENT_HEAP_ALLOC_ALIGN);
|
||||||
|
|
||||||
RB_VM_LOCK_ENTER();
|
TH_ASSERT(RB_TYPE_P(obj, T_ARRAY) ||
|
||||||
{
|
RB_TYPE_P(obj, T_OBJECT) ||
|
||||||
struct transient_heap* theap = transient_heap_get();
|
RB_TYPE_P(obj, T_STRUCT) ||
|
||||||
size_t size = ROUND_UP(req_size + sizeof(struct transient_alloc_header), TRANSIENT_HEAP_ALLOC_ALIGN);
|
RB_TYPE_P(obj, T_HASH)); /* supported types */
|
||||||
|
|
||||||
TH_ASSERT(RB_TYPE_P(obj, T_ARRAY) ||
|
if (size > TRANSIENT_HEAP_ALLOC_MAX) {
|
||||||
RB_TYPE_P(obj, T_OBJECT) ||
|
if (TRANSIENT_HEAP_DEBUG >= 3) fprintf(stderr, "rb_transient_heap_alloc: [too big: %ld] %s\n", (long)size, rb_obj_info(obj));
|
||||||
RB_TYPE_P(obj, T_STRUCT) ||
|
ret = NULL;
|
||||||
RB_TYPE_P(obj, T_HASH)); /* supported types */
|
}
|
||||||
|
|
||||||
if (size > TRANSIENT_HEAP_ALLOC_MAX) {
|
|
||||||
if (TRANSIENT_HEAP_DEBUG >= 3) fprintf(stderr, "rb_transient_heap_alloc: [too big: %ld] %s\n", (long)size, rb_obj_info(obj));
|
|
||||||
ret = NULL;
|
|
||||||
}
|
|
||||||
#if TRANSIENT_HEAP_DEBUG_DONT_PROMOTE == 0
|
#if TRANSIENT_HEAP_DEBUG_DONT_PROMOTE == 0
|
||||||
else if (RB_OBJ_PROMOTED_RAW(obj)) {
|
else if (RB_OBJ_PROMOTED_RAW(obj)) {
|
||||||
if (TRANSIENT_HEAP_DEBUG >= 3) fprintf(stderr, "rb_transient_heap_alloc: [promoted object] %s\n", rb_obj_info(obj));
|
if (TRANSIENT_HEAP_DEBUG >= 3) fprintf(stderr, "rb_transient_heap_alloc: [promoted object] %s\n", rb_obj_info(obj));
|
||||||
ret = NULL;
|
ret = NULL;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
else if (RBASIC_CLASS(obj) == 0) {
|
else if (RBASIC_CLASS(obj) == 0) {
|
||||||
if (TRANSIENT_HEAP_DEBUG >= 3) fprintf(stderr, "rb_transient_heap_alloc: [hidden object] %s\n", rb_obj_info(obj));
|
if (TRANSIENT_HEAP_DEBUG >= 3) fprintf(stderr, "rb_transient_heap_alloc: [hidden object] %s\n", rb_obj_info(obj));
|
||||||
ret = NULL;
|
ret = NULL;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
else {
|
else {
|
||||||
struct transient_alloc_header *header = transient_heap_allocatable_header(theap, size);
|
struct transient_alloc_header *header = transient_heap_allocatable_header(theap, size);
|
||||||
if (header) {
|
if (header) {
|
||||||
void *ptr;
|
void *ptr;
|
||||||
|
|
||||||
/* header is poisoned to prevent buffer overflow, should
|
/* header is poisoned to prevent buffer overflow, should
|
||||||
* unpoison first... */
|
* unpoison first... */
|
||||||
asan_unpoison_memory_region(header, sizeof *header, true);
|
asan_unpoison_memory_region(header, sizeof *header, true);
|
||||||
|
|
||||||
header->size = size;
|
header->size = size;
|
||||||
header->magic = TRANSIENT_HEAP_ALLOC_MAGIC;
|
header->magic = TRANSIENT_HEAP_ALLOC_MAGIC;
|
||||||
header->next_marked_index = TRANSIENT_HEAP_ALLOC_MARKING_FREE;
|
header->next_marked_index = TRANSIENT_HEAP_ALLOC_MARKING_FREE;
|
||||||
header->obj = obj; /* TODO: can we eliminate it? */
|
header->obj = obj; /* TODO: can we eliminate it? */
|
||||||
|
|
||||||
/* header is fixed; shall poison again */
|
/* header is fixed; shall poison again */
|
||||||
asan_poison_memory_region(header, sizeof *header);
|
asan_poison_memory_region(header, sizeof *header);
|
||||||
ptr = header + 1;
|
ptr = header + 1;
|
||||||
|
|
||||||
theap->total_objects++; /* statistics */
|
theap->total_objects++; /* statistics */
|
||||||
|
|
||||||
#if TRANSIENT_HEAP_DEBUG_DONT_PROMOTE
|
#if TRANSIENT_HEAP_DEBUG_DONT_PROMOTE
|
||||||
if (RB_OBJ_PROMOTED_RAW(obj)) {
|
if (RB_OBJ_PROMOTED_RAW(obj)) {
|
||||||
transient_heap_promote_add(theap, obj);
|
transient_heap_promote_add(theap, obj);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if (TRANSIENT_HEAP_DEBUG >= 3) fprintf(stderr, "rb_transient_heap_alloc: header:%p ptr:%p size:%d obj:%s\n", (void *)header, ptr, (int)size, rb_obj_info(obj));
|
if (TRANSIENT_HEAP_DEBUG >= 3) fprintf(stderr, "rb_transient_heap_alloc: header:%p ptr:%p size:%d obj:%s\n", (void *)header, ptr, (int)size, rb_obj_info(obj));
|
||||||
|
|
||||||
RB_DEBUG_COUNTER_INC(theap_alloc);
|
RB_DEBUG_COUNTER_INC(theap_alloc);
|
||||||
|
|
||||||
/* ptr is set up; OK to unpoison. */
|
/* ptr is set up; OK to unpoison. */
|
||||||
asan_unpoison_memory_region(ptr, size - sizeof *header, true);
|
asan_unpoison_memory_region(ptr, size - sizeof *header, true);
|
||||||
ret = ptr;
|
ret = ptr;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (TRANSIENT_HEAP_DEBUG >= 3) fprintf(stderr, "rb_transient_heap_alloc: [no enough space: %ld] %s\n", (long)size, rb_obj_info(obj));
|
if (TRANSIENT_HEAP_DEBUG >= 3) fprintf(stderr, "rb_transient_heap_alloc: [no enough space: %ld] %s\n", (long)size, rb_obj_info(obj));
|
||||||
RB_DEBUG_COUNTER_INC(theap_alloc_fail);
|
RB_DEBUG_COUNTER_INC(theap_alloc_fail);
|
||||||
ret = NULL;
|
ret = NULL;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
RB_VM_LOCK_LEAVE();
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -775,16 +773,16 @@ transient_heap_update_status(struct transient_heap* theap, enum transient_heap_s
|
||||||
static void
|
static void
|
||||||
transient_heap_evacuate(void *dmy)
|
transient_heap_evacuate(void *dmy)
|
||||||
{
|
{
|
||||||
RB_VM_LOCK_ENTER();
|
struct transient_heap* theap = transient_heap_get();
|
||||||
rb_vm_barrier();
|
|
||||||
{
|
|
||||||
struct transient_heap* theap = transient_heap_get();
|
|
||||||
|
|
||||||
if (theap->status == transient_heap_marking) {
|
if (theap->total_marked_objects == 0) return;
|
||||||
if (TRANSIENT_HEAP_DEBUG >= 1) fprintf(stderr, "!! transient_heap_evacuate: skip while transient_heap_marking\n");
|
if (ruby_single_main_ractor == NULL) rb_bug("not single ractor mode");
|
||||||
}
|
if (theap->status == transient_heap_marking) {
|
||||||
else {
|
if (TRANSIENT_HEAP_DEBUG >= 1) fprintf(stderr, "!! transient_heap_evacuate: skip while transient_heap_marking\n");
|
||||||
VALUE gc_disabled = rb_gc_disable_no_rest();
|
}
|
||||||
|
else {
|
||||||
|
VALUE gc_disabled = rb_gc_disable_no_rest();
|
||||||
|
{
|
||||||
struct transient_heap_block* block;
|
struct transient_heap_block* block;
|
||||||
|
|
||||||
RUBY_DEBUG_LOG("start gc_disabled:%d", RTEST(gc_disabled));
|
RUBY_DEBUG_LOG("start gc_disabled:%d", RTEST(gc_disabled));
|
||||||
|
@ -825,12 +823,16 @@ transient_heap_evacuate(void *dmy)
|
||||||
|
|
||||||
transient_heap_verify(theap);
|
transient_heap_verify(theap);
|
||||||
transient_heap_update_status(theap, transient_heap_none);
|
transient_heap_update_status(theap, transient_heap_none);
|
||||||
|
|
||||||
if (gc_disabled != Qtrue) rb_gc_enable();
|
|
||||||
RUBY_DEBUG_LOG("finish", 0);
|
|
||||||
}
|
}
|
||||||
|
if (gc_disabled != Qtrue) rb_gc_enable();
|
||||||
|
RUBY_DEBUG_LOG("finish", 0);
|
||||||
}
|
}
|
||||||
RB_VM_LOCK_LEAVE();
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
rb_transient_heap_evacuate(void)
|
||||||
|
{
|
||||||
|
transient_heap_evacuate(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -964,9 +966,9 @@ rb_transient_heap_finish_marking(void)
|
||||||
|
|
||||||
struct transient_heap* theap = transient_heap_get();
|
struct transient_heap* theap = transient_heap_get();
|
||||||
|
|
||||||
if (TRANSIENT_HEAP_DEBUG >= 1) fprintf(stderr, "!! rb_transient_heap_finish_marking objects:%d, marked:%d\n",
|
RUBY_DEBUG_LOG("objects:%d, marked:%d",
|
||||||
theap->total_objects,
|
theap->total_objects,
|
||||||
theap->total_marked_objects);
|
theap->total_marked_objects);
|
||||||
if (TRANSIENT_HEAP_DEBUG >= 2) transient_heap_dump(theap);
|
if (TRANSIENT_HEAP_DEBUG >= 2) transient_heap_dump(theap);
|
||||||
|
|
||||||
TH_ASSERT(theap->total_objects >= theap->total_marked_objects);
|
TH_ASSERT(theap->total_objects >= theap->total_marked_objects);
|
||||||
|
|
|
@ -31,6 +31,9 @@ void rb_transient_heap_start_marking(int full_marking);
|
||||||
void rb_transient_heap_finish_marking(void);
|
void rb_transient_heap_finish_marking(void);
|
||||||
void rb_transient_heap_update_references(void);
|
void rb_transient_heap_update_references(void);
|
||||||
|
|
||||||
|
/* used by ractor.c */
|
||||||
|
void rb_transient_heap_evacuate(void);
|
||||||
|
|
||||||
/* for debug API */
|
/* for debug API */
|
||||||
void rb_transient_heap_dump(void);
|
void rb_transient_heap_dump(void);
|
||||||
void rb_transient_heap_verify(void);
|
void rb_transient_heap_verify(void);
|
||||||
|
@ -49,6 +52,7 @@ void rb_struct_transient_heap_evacuate(VALUE st, int promote);
|
||||||
#define rb_transient_heap_promote(obj) ((void)0)
|
#define rb_transient_heap_promote(obj) ((void)0)
|
||||||
#define rb_transient_heap_start_marking(full_marking) ((void)0)
|
#define rb_transient_heap_start_marking(full_marking) ((void)0)
|
||||||
#define rb_transient_heap_update_references() ((void)0)
|
#define rb_transient_heap_update_references() ((void)0)
|
||||||
|
#define rb_transient_heap_evacuate() ((void)0)
|
||||||
#define rb_transient_heap_finish_marking() ((void)0)
|
#define rb_transient_heap_finish_marking() ((void)0)
|
||||||
#define rb_transient_heap_mark(obj, ptr) ((void)0)
|
#define rb_transient_heap_mark(obj, ptr) ((void)0)
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче