[前][次][番号順一覧][スレッド一覧]

ruby-changes:63776

From: Takashi <ko1@a...>
Date: Sat, 28 Nov 2020 14:39:28 +0900 (JST)
Subject: [ruby-changes:63776] 16dab6b692 (master): Run unload_units in the JIT worker thread

https://git.ruby-lang.org/ruby.git/commit/?id=16dab6b692

From 16dab6b69263ed9c816bc0283c8c1f2f95dc1027 Mon Sep 17 00:00:00 2001
From: Takashi Kokubun <takashikkbn@g...>
Date: Fri, 27 Nov 2020 21:28:57 -0800
Subject: Run unload_units in the JIT worker thread

to avoid "Too many JIT code, but skipped unloading units for JIT compaction".
Now we can forget the `in_compact` locking.

Moving some functions from mjit.c to mjit_worker.c because mjit_worker.c
should have functions executed in the JIT worker.

diff --git a/mjit.c b/mjit.c
index 170f223..94857df 100644
--- a/mjit.c
+++ b/mjit.c
@@ -166,16 +166,6 @@ free_list(struct rb_mjit_unit_list *list, bool close_handle_p) https://github.com/ruby/ruby/blob/trunk/mjit.c#L166
     list->length = 0;
 }
 
-// MJIT info related to an existing continutaion.
-struct mjit_cont {
-    rb_execution_context_t *ec; // continuation ec
-    struct mjit_cont *prev, *next; // used to form lists
-};
-
-// Double linked list of registered continuations. This is used to detect
-// units which are in use in unload_units.
-static struct mjit_cont *first_cont;
-
 // Register a new continuation with execution context `ec`. Return MJIT info about
 // the continuation.
 struct mjit_cont *
@@ -253,87 +243,6 @@ create_unit(const rb_iseq_t *iseq) https://github.com/ruby/ruby/blob/trunk/mjit.c#L243
     iseq->body->jit_unit = unit;
 }
 
-// Set up field `used_code_p` for unit iseqs whose iseq on the stack of ec.
-static void
-mark_ec_units(rb_execution_context_t *ec)
-{
-    const rb_control_frame_t *cfp;
-
-    if (ec->vm_stack == NULL)
-        return;
-    for (cfp = RUBY_VM_END_CONTROL_FRAME(ec) - 1; ; cfp = RUBY_VM_NEXT_CONTROL_FRAME(cfp)) {
-        const rb_iseq_t *iseq;
-        if (cfp->pc && (iseq = cfp->iseq) != NULL
-            && imemo_type((VALUE) iseq) == imemo_iseq
-            && (iseq->body->jit_unit) != NULL) {
-            iseq->body->jit_unit->used_code_p = true;
-        }
-
-        if (cfp == ec->cfp)
-            break; // reached the most recent cfp
-    }
-}
-
-// Unload JIT code of some units to satisfy the maximum permitted
-// number of units with a loaded code.
-static void
-unload_units(void)
-{
-    struct rb_mjit_unit *unit = 0, *next, *worst;
-    struct mjit_cont *cont;
-    int delete_num, units_num = active_units.length;
-
-    // For now, we don't unload units when ISeq is GCed. We should
-    // unload such ISeqs first here.
-    list_for_each_safe(&active_units.head, unit, next, unode) {
-        if (unit->iseq == NULL) { // ISeq is GCed.
-            remove_from_list(unit, &active_units);
-            free_unit(unit);
-        }
-    }
-
-    // Detect units which are in use and can't be unloaded.
-    list_for_each(&active_units.head, unit, unode) {
-        assert(unit->iseq != NULL && unit->handle != NULL);
-        unit->used_code_p = false;
-    }
-    // All threads have a root_fiber which has a mjit_cont. Other normal fibers also
-    // have a mjit_cont. Thus we can check ISeqs in use by scanning ec of mjit_conts.
-    for (cont = first_cont; cont != NULL; cont = cont->next) {
-        mark_ec_units(cont->ec);
-    }
-    // TODO: check stale_units and unload unused ones! (note that the unit is not associated to ISeq anymore)
-
-    // Remove 1/10 units more to decrease unloading calls.
-    // TODO: Calculate max total_calls in unit_queue and don't unload units
-    // whose total_calls are larger than the max.
-    delete_num = active_units.length / 10;
-    for (; active_units.length > mjit_opts.max_cache_size - delete_num;) {
-        // Find one unit that has the minimum total_calls.
-        worst = NULL;
-        list_for_each(&active_units.head, unit, unode) {
-            if (unit->used_code_p) // We can't unload code on stack.
-                continue;
-
-            if (worst == NULL || worst->iseq->body->total_calls > unit->iseq->body->total_calls) {
-                worst = unit;
-            }
-        }
-        if (worst == NULL)
-            break;
-
-        // Unload the worst node.
-        verbose(2, "Unloading unit %d (calls=%lu)", worst->id, worst->iseq->body->total_calls);
-        assert(worst->handle != NULL);
-        remove_from_list(worst, &active_units);
-        free_unit(worst);
-    }
-
-    if (units_num > active_units.length) {
-        verbose(1, "Too many JIT code -- %d units unloaded", units_num - active_units.length);
-    }
-}
-
 static void
 mjit_add_iseq_to_process(const rb_iseq_t *iseq, const struct rb_mjit_compile_info *compile_info)
 {
@@ -352,16 +261,7 @@ mjit_add_iseq_to_process(const rb_iseq_t *iseq, const struct rb_mjit_compile_inf https://github.com/ruby/ruby/blob/trunk/mjit.c#L261
     CRITICAL_SECTION_START(3, "in add_iseq_to_process");
     add_to_list(iseq->body->jit_unit, &unit_queue);
     if (active_units.length >= mjit_opts.max_cache_size) {
-        if (in_compact) {
-            verbose(1, "Too many JIT code, but skipped unloading units for JIT compaction");
-        } else {
-            RB_DEBUG_COUNTER_INC(mjit_unload_units);
-            unload_units();
-        }
-        if (active_units.length == mjit_opts.max_cache_size && mjit_opts.wait) { // Sometimes all methods may be in use
-            mjit_opts.max_cache_size++; // avoid infinite loop on `rb_mjit_wait_call`. Note that --jit-wait is just for testing.
-            verbose(1, "No units can be unloaded -- incremented max-cache-size to %d for --jit-wait", mjit_opts.max_cache_size);
-        }
+        unload_units_p = true;
     }
     verbose(3, "Sending wakeup signal to workers in mjit_add_iseq_to_process");
     rb_native_cond_broadcast(&mjit_worker_wakeup);
diff --git a/mjit_worker.c b/mjit_worker.c
index a1b565d..ff03a1a 100644
--- a/mjit_worker.c
+++ b/mjit_worker.c
@@ -225,8 +225,8 @@ static rb_nativethread_cond_t mjit_gc_wakeup; https://github.com/ruby/ruby/blob/trunk/mjit_worker.c#L225
 static int in_gc = 0;
 // True when JIT is working.
 static bool in_jit = false;
-// True when JIT compaction is running.
-static bool in_compact = false;
+// True when unload_units is requested from Ruby threads.
+static bool unload_units_p = false;
 // Set to true to stop worker.
 static bool stop_worker_p;
 // Set to true if worker is stopped.
@@ -973,10 +973,6 @@ compact_all_jit_code(void) https://github.com/ruby/ruby/blob/trunk/mjit_worker.c#L973
     sprint_uniq_filename(c_file, (int)sizeof(c_file), unit->id, MJIT_TMP_PREFIX, c_ext);
     sprint_uniq_filename(so_file, (int)sizeof(so_file), unit->id, MJIT_TMP_PREFIX, so_ext);
 
-    CRITICAL_SECTION_START(3, "in compact_all_jit_code to guard .c files from unload_units");
-    in_compact = true;
-    CRITICAL_SECTION_FINISH(3, "in compact_all_jit_code to guard .c files from unload_units");
-
     bool success = compile_compact_jit_code(c_file);
     double start_time = real_ms_time();
     if (success) {
@@ -986,10 +982,6 @@ compact_all_jit_code(void) https://github.com/ruby/ruby/blob/trunk/mjit_worker.c#L982
     }
     double end_time = real_ms_time();
 
-    CRITICAL_SECTION_START(3, "in compact_all_jit_code to release .c files");
-    in_compact = false;
-    CRITICAL_SECTION_FINISH(3, "in compact_all_jit_code to release .c files");
-
     if (success) {
         void *handle = dlopen(so_file, RTLD_NOW);
         if (handle == NULL) {
@@ -1228,6 +1220,97 @@ mjit_capture_cc_entries(const struct rb_iseq_constant_body *compiled_iseq, const https://github.com/ruby/ruby/blob/trunk/mjit_worker.c#L1220
     return cc_entries_index;
 }
 
+// Set up field `used_code_p` for unit iseqs whose iseq on the stack of ec.
+static void
+mark_ec_units(rb_execution_context_t *ec)
+{
+    const rb_control_frame_t *cfp;
+
+    if (ec->vm_stack == NULL)
+        return;
+    for (cfp = RUBY_VM_END_CONTROL_FRAME(ec) - 1; ; cfp = RUBY_VM_NEXT_CONTROL_FRAME(cfp)) {
+        const rb_iseq_t *iseq;
+        if (cfp->pc && (iseq = cfp->iseq) != NULL
+            && imemo_type((VALUE) iseq) == imemo_iseq
+            && (iseq->body->jit_unit) != NULL) {
+            iseq->body->jit_unit->used_code_p = true;
+        }
+
+        if (cfp == ec->cfp)
+            break; // reached the most recent cfp
+    }
+}
+
+// MJIT info related to an existing continutaion.
+struct mjit_cont {
+    rb_execution_context_t *ec; // continuation ec
+    struct mjit_cont *prev, *next; // used to form lists
+};
+
+// Double linked list of registered continuations. This is used to detect
+// units which are in use in unload_units.
+static struct mjit_cont *first_cont;
+
+// Unload JIT code of some units to satisfy the maximum permitted
+// number of units with a loaded code.
+static void
+unload_units(void)
+{
+    struct rb_mjit_unit *unit = 0, *next, *worst;
+    struct mjit_cont *cont;
+    int delete_num, units_num = active_units.length;
+
+    // For now, we don't unload units when ISeq is GCed. We should
+    // unload such ISeqs first here.
+    list_for_each_safe(&active_units.head, unit, next, unode) {
+        if (unit->iseq == NULL) { // ISeq is GCed.
+            remove_from_list(unit, &active_units);
+            free_unit(unit);
+        }
+    }
+
+    // Detect units which are in use and can't be unloaded.
+    list_for_each(&active_units.head, unit, unode) {
+        assert(unit->iseq != NULL && unit->handle != NULL);
+        unit->used_code_p = false;
+    }
+    // All threads have a root_fiber which has a mjit_cont. Other normal fibers also
+    // have a mjit_cont. Thus we can check ISeqs in use by scanning ec of mjit_conts.
+    for (cont = first_cont; cont != NULL; cont = cont->next) {
+        mark_ec_units(cont->ec);
+    }
+    // TODO: check stale_units and unload unused ones! (note that the unit is not associated to ISeq anymore)
+
+    // Remove 1/10 units more to decrease unloading calls.
+    // TODO: Calculate max total_calls in unit_queue and don't unload units
+    // whose total_calls are larger than the max.
+    delete_num = active_units.length / 10;
+    for (; active_units.length > mjit_opts.max_cache_size - delete_num;) {
+        // Fi (... truncated)

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]