-
-
Save kosaki/000b3b46004dd6f1156f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
From c5c39df34ca9303b7cf47627abf7a5e7854bea4d Mon Sep 17 00:00:00 2001 | |
From: KOSAKI Motohiro <kosaki.motohiro@gmail.com> | |
Date: Wed, 4 May 2011 20:55:38 +0900 | |
Subject: [PATCH] gvl improve | |
Signed-off-by: KOSAKI Motohiro <kosaki.motohiro@gmail.com> | |
--- | |
thread.c | 49 +++++++++++++++++----------- | |
thread_pthread.c | 92 +++++++++++++++++++++++++++++++++++++++++++++++++++--- | |
thread_pthread.h | 13 ++++++- | |
thread_win32.c | 5 ++- | |
vm_core.h | 5 ++- | |
5 files changed, 135 insertions(+), 29 deletions(-) | |
diff --git a/thread.c b/thread.c | |
index 1a3dabe..a72734c 100644 | |
--- a/thread.c | |
+++ b/thread.c | |
@@ -1014,7 +1014,7 @@ rb_thread_sleep(int sec) | |
static void rb_threadptr_execute_interrupts_rec(rb_thread_t *, int); | |
static void | |
-rb_thread_schedule_rec(int sched_depth) | |
+rb_thread_schedule_rec(int sched_depth, unsigned long limits_ms) | |
{ | |
thread_debug("rb_thread_schedule\n"); | |
if (!rb_thread_alone()) { | |
@@ -1023,11 +1023,19 @@ rb_thread_schedule_rec(int sched_depth) | |
thread_debug("rb_thread_schedule/switch start\n"); | |
RB_GC_SAVE_MACHINE_CONTEXT(th); | |
+ | |
+#if HAVE_GVL_YIELD | |
+ { | |
+ if (th->running_time_ms >= limits_ms) | |
+ gvl_yield(th->vm, th); | |
+ } | |
+#else | |
gvl_release(th->vm); | |
{ | |
native_thread_yield(); | |
} | |
gvl_acquire(th->vm, th); | |
+#endif | |
rb_thread_set_current(th); | |
thread_debug("rb_thread_schedule/switch done\n"); | |
@@ -1041,7 +1049,13 @@ rb_thread_schedule_rec(int sched_depth) | |
void | |
rb_thread_schedule(void) | |
{ | |
- rb_thread_schedule_rec(0); | |
+ rb_thread_schedule_rec(0, 0); | |
+} | |
+ | |
+void | |
+rb_thread_schedule_limit(unsigned long limits_ms) | |
+{ | |
+ rb_thread_schedule_rec(0, limits_ms); | |
} | |
/* blocking region */ | |
@@ -1278,7 +1292,8 @@ ruby_thread_has_gvl_p(void) | |
static VALUE | |
thread_s_pass(VALUE klass) | |
{ | |
- rb_thread_schedule(); | |
+ /* We need to avoid Thread.pass flood issue. It may hurt a performance. */ | |
+ rb_thread_schedule_limit(100); | |
return Qnil; | |
} | |
@@ -1331,23 +1346,20 @@ rb_threadptr_execute_interrupts_rec(rb_thread_t *th, int sched_depth) | |
} | |
if (!sched_depth && timer_interrupt) { | |
- sched_depth++; | |
+ unsigned long limits_ms = 250; | |
+ | |
+ if (th->priority > 0) | |
+ limits_ms <<= th->priority; | |
+ else | |
+ limits_ms >>= -th->priority; | |
+ | |
+ if (status == THREAD_RUNNABLE) | |
+ th->running_time_ms += TIMER_THREAD_DURATION; | |
+ | |
+ sched_depth++; | |
EXEC_EVENT_HOOK(th, RUBY_EVENT_SWITCH, th->cfp->self, 0, 0); | |
- if (th->slice > 0) { | |
- th->slice--; | |
- } | |
- else { | |
- reschedule: | |
- rb_thread_schedule_rec(sched_depth+1); | |
- if (th->slice < 0) { | |
- th->slice++; | |
- goto reschedule; | |
- } | |
- else { | |
- th->slice = th->priority; | |
- } | |
- } | |
+ rb_thread_schedule_rec(sched_depth+1, limits_ms); | |
} | |
} | |
} | |
@@ -2283,7 +2295,6 @@ rb_thread_priority_set(VALUE thread, VALUE prio) | |
priority = RUBY_THREAD_PRIORITY_MIN; | |
} | |
th->priority = priority; | |
- th->slice = priority; | |
#endif | |
return INT2NUM(th->priority); | |
} | |
diff --git a/thread_pthread.c b/thread_pthread.c | |
index 4161f66..5c94721 100644 | |
--- a/thread_pthread.c | |
+++ b/thread_pthread.c | |
@@ -34,9 +34,10 @@ static void native_cond_destroy(pthread_cond_t *cond); | |
native_mutex_initialize(lock), \ | |
native_mutex_lock(lock)) | |
-#define GVL_SIMPLE_LOCK 0 | |
+#define GVL_SIMPLE_LOCK 9 | |
#define GVL_DEBUG 0 | |
+#if !GVL_SIMPLE_LOCK | |
static void | |
gvl_show_waiting_threads(rb_vm_t *vm) | |
{ | |
@@ -48,7 +49,6 @@ gvl_show_waiting_threads(rb_vm_t *vm) | |
} | |
} | |
-#if !GVL_SIMPLE_LOCK | |
static void | |
gvl_waiting_push(rb_vm_t *vm, rb_thread_t *th) | |
{ | |
@@ -75,10 +75,36 @@ gvl_waiting_shift(rb_vm_t *vm, rb_thread_t *th) | |
#endif | |
static void | |
+gvl_acquire_locked(rb_vm_t *vm) | |
+{ | |
+ | |
+ if (vm->gvl.acquired) { | |
+ vm->gvl.waiting++; | |
+ while (vm->gvl.acquired) { | |
+ native_cond_wait(&vm->gvl.cond, &vm->gvl.lock); | |
+ } | |
+ vm->gvl.waiting--; | |
+ | |
+ if (vm->gvl.need_yield) { | |
+ vm->gvl.need_yield = 0; | |
+ native_cond_signal(&vm->gvl.switch_cond); | |
+ } | |
+ } | |
+ | |
+ vm->gvl.acquired = 1; | |
+} | |
+ | |
+static void | |
gvl_acquire(rb_vm_t *vm, rb_thread_t *th) | |
{ | |
#if GVL_SIMPLE_LOCK | |
+ #if GVL_SIMPLE_LOCK==1 | |
native_mutex_lock(&vm->gvl.lock); | |
+ #elif GVL_SIMPLE_LOCK==9 | |
+ native_mutex_lock(&vm->gvl.lock); | |
+ gvl_acquire_locked(vm); | |
+ native_mutex_unlock(&vm->gvl.lock); | |
+ #endif | |
#else | |
native_mutex_lock(&vm->gvl.lock); | |
if (vm->gvl.waiting > 0 || vm->gvl.acquired != 0) { | |
@@ -96,16 +122,30 @@ gvl_acquire(rb_vm_t *vm, rb_thread_t *th) | |
} | |
vm->gvl.acquired = 1; | |
native_mutex_unlock(&vm->gvl.lock); | |
-#endif | |
if (GVL_DEBUG) gvl_show_waiting_threads(vm); | |
if (GVL_DEBUG) fprintf(stderr, "gvl acquire (%p): acquire\n", (void *)th); | |
+#endif | |
+} | |
+ | |
+static void | |
+gvl_release_locked(rb_vm_t *vm) | |
+{ | |
+ vm->gvl.acquired = 0; | |
+ if (vm->gvl.waiting > 0) | |
+ native_cond_signal(&vm->gvl.cond); | |
} | |
static void | |
gvl_release(rb_vm_t *vm) | |
{ | |
#if GVL_SIMPLE_LOCK | |
+ #if GVL_SIMPLE_LOCK==1 | |
native_mutex_unlock(&vm->gvl.lock); | |
+ #elif GVL_SIMPLE_LOCK==9 | |
+ native_mutex_lock(&vm->gvl.lock); | |
+ gvl_release_locked(vm); | |
+ native_mutex_unlock(&vm->gvl.lock); | |
+ #endif | |
#else | |
native_mutex_lock(&vm->gvl.lock); | |
if (vm->gvl.waiting > 0) { | |
@@ -122,13 +162,53 @@ gvl_release(rb_vm_t *vm) | |
#endif | |
} | |
+#if GVL_SIMPLE_LOCK == 9 | |
+#define HAVE_GVL_YIELD 1 | |
+static void | |
+gvl_yield(rb_vm_t *vm, rb_thread_t *th) | |
+{ | |
+ native_mutex_lock(&vm->gvl.lock); | |
+ | |
+ /* An another thread is processing GVL yield. */ | |
+ if (vm->gvl.need_yield) { | |
+ native_mutex_unlock(&vm->gvl.lock); | |
+ return; | |
+ } | |
+ | |
+ if (vm->gvl.waiting > 0) | |
+ vm->gvl.need_yield = 1; | |
+ | |
+ gvl_release_locked(vm); | |
+ if (vm->gvl.need_yield) { | |
+ /* Wait until another thread task take GVL. */ | |
+ native_cond_wait(&vm->gvl.switch_cond, &vm->gvl.lock); | |
+ } else { | |
+ native_mutex_unlock(&vm->gvl.lock); | |
+ sched_yield(); | |
+ native_mutex_lock(&vm->gvl.lock); | |
+ } | |
+ | |
+ gvl_acquire_locked(vm); | |
+ native_mutex_unlock(&vm->gvl.lock); | |
+} | |
+#endif | |
+ | |
static void | |
gvl_init(rb_vm_t *vm) | |
{ | |
if (GVL_DEBUG) fprintf(stderr, "gvl init\n"); | |
#if GVL_SIMPLE_LOCK | |
+ #if GVL_SIMPLE_LOCK==1 | |
+ native_mutex_initialize(&vm->gvl.lock); | |
+ #elif GVL_SIMPLE_LOCK==9 | |
native_mutex_initialize(&vm->gvl.lock); | |
+ native_cond_initialize(&vm->gvl.cond); | |
+ native_cond_initialize(&vm->gvl.switch_cond); | |
+ vm->gvl.acquired = 0; | |
+ vm->gvl.waiting = 0; | |
+ vm->gvl.need_yield = 0; | |
+ #endif | |
#else | |
native_mutex_initialize(&vm->gvl.lock); | |
vm->gvl.waiting_threads = 0; | |
@@ -917,6 +997,8 @@ get_ts(struct timespec *ts, unsigned long nsec) | |
return ts; | |
} | |
+#define TIMER_THREAD_DURATION (50) /* milliseconds */ | |
+ | |
static void * | |
thread_timer(void *dummy) | |
{ | |
@@ -924,9 +1006,9 @@ thread_timer(void *dummy) | |
native_mutex_lock(&timer_thread_lock); | |
native_cond_broadcast(&timer_thread_cond); | |
-#define WAIT_FOR_10MS() native_cond_timedwait(&timer_thread_cond, &timer_thread_lock, get_ts(&ts, PER_NANO/100)) | |
while (system_working > 0) { | |
- int err = WAIT_FOR_10MS(); | |
+ int err = native_cond_timedwait(&timer_thread_cond, &timer_thread_lock, | |
+ get_ts(&ts, TIMER_THREAD_DURATION*1000*1000)); | |
if (err == ETIMEDOUT); | |
else if (err == 0 || err == EINTR) { | |
if (rb_signal_buff_size() == 0) break; | |
diff --git a/thread_pthread.h b/thread_pthread.h | |
index 3742cb2..d82c08a 100644 | |
--- a/thread_pthread.h | |
+++ b/thread_pthread.h | |
@@ -29,11 +29,20 @@ typedef struct native_thread_data_struct { | |
#include <semaphore.h> | |
typedef struct rb_global_vm_lock_struct { | |
+ /* fast path */ | |
+ unsigned long acquired; | |
pthread_mutex_t lock; | |
+ | |
+ /* slow path */ | |
+ unsigned long waiting; | |
+ pthread_cond_t cond; | |
+ | |
+ /* yield */ | |
+ pthread_cond_t switch_cond; | |
+ unsigned long need_yield; | |
+ | |
struct rb_thread_struct * volatile waiting_threads; | |
struct rb_thread_struct *waiting_last_thread; | |
- int waiting; | |
- int volatile acquired; | |
} rb_global_vm_lock_t; | |
#endif /* RUBY_THREAD_PTHREAD_H */ | |
diff --git a/thread_win32.c b/thread_win32.c | |
index e29c6af..fe96ff7 100644 | |
--- a/thread_win32.c | |
+++ b/thread_win32.c | |
@@ -13,7 +13,8 @@ | |
#include <process.h> | |
-#define WIN32_WAIT_TIMEOUT 10 /* 10 ms */ | |
+#define TIMER_THREAD_DURATION (10) /* 10 ms */ | |
+ | |
#undef Sleep | |
#define native_thread_yield() Sleep(0) | |
@@ -626,7 +627,7 @@ static unsigned long _stdcall | |
timer_thread_func(void *dummy) | |
{ | |
thread_debug("timer_thread\n"); | |
- while (WaitForSingleObject(timer_thread_lock, WIN32_WAIT_TIMEOUT) == | |
+ while (WaitForSingleObject(timer_thread_lock, TIMER_THREAD_DURATION) == | |
WAIT_TIMEOUT) { | |
timer_thread_function(dummy); | |
} | |
diff --git a/vm_core.h b/vm_core.h | |
index c5417d9..9061dc8 100644 | |
--- a/vm_core.h | |
+++ b/vm_core.h | |
@@ -418,7 +418,6 @@ typedef struct rb_thread_struct { | |
rb_thread_id_t thread_id; | |
enum rb_thread_status status; | |
int priority; | |
- int slice; | |
native_thread_data_t native_thread_data; | |
void *blocking_region_buffer; | |
@@ -484,6 +483,7 @@ typedef struct rb_thread_struct { | |
#ifdef USE_SIGALTSTACK | |
void *altstack; | |
#endif | |
+ unsigned long running_time_ms; | |
} rb_thread_t; | |
/* iseq.c */ | |
@@ -679,6 +679,9 @@ extern rb_vm_t *ruby_current_vm; | |
#define GET_THREAD() ruby_current_thread | |
#define rb_thread_set_current_raw(th) (void)(ruby_current_thread = (th)) | |
#define rb_thread_set_current(th) do { \ | |
+ if ((th)->vm->running_thread != (th)) { \ | |
+ (th)->vm->running_thread->running_time_ms = 0; \ | |
+ } \ | |
rb_thread_set_current_raw(th); \ | |
(th)->vm->running_thread = (th); \ | |
} while (0) | |
-- | |
1.7.4 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment