From b5d417eed926f99e47e87962eae868ca0b8ffc8c Mon Sep 17 00:00:00 2001 From: Todd Veldhuizen Date: Tue, 25 Jul 2023 11:27:57 -0400 Subject: [PATCH 1/5] scheduler metrics for v1.8.2+RAI --- base/lock.jl | 3 +++ base/partr.jl | 5 +++++ base/task.jl | 8 ++++++++ base/threadingconstructs.jl | 1 + src/jl_exported_funcs.inc | 4 ++++ src/julia_internal.h | 9 +++++++++ src/partr.c | 16 ++++++++++++++-- src/task.c | 6 ++++++ src/threading.c | 34 ++++++++++++++++++++++++++++++++++ 9 files changed, 84 insertions(+), 2 deletions(-) diff --git a/base/lock.jl b/base/lock.jl index 9057a39294229..85c35dca10530 100644 --- a/base/lock.jl +++ b/base/lock.jl @@ -84,6 +84,7 @@ end #@assert rl.reentrancy_cnt === 0 rl.reentrancy_cnt = 0x0000_0001 @atomic :release rl.locked_by = ct + # TODO: returning without enable_finalizers() ?? return true end GC.enable_finalizers() @@ -109,6 +110,8 @@ Each `lock` must be matched by an [`unlock`](@ref). # it was unlocked, so try to lock it ourself _trylock(rl, current_task()) && break else # it was locked, so now wait for the release to notify us + # TODO: verify it is impossible for havelock == 0x00 before + # waiting here. wait(c) end end diff --git a/base/partr.jl b/base/partr.jl index 0393308802281..9b55ca9d0f0ae 100644 --- a/base/partr.jl +++ b/base/partr.jl @@ -94,12 +94,16 @@ end function multiq_insert(task::Task, priority::UInt16) + ccall(:jl_tv_multiq_p_inc, Cvoid, ()) + + # tpid = task pool id tpid = ccall(:jl_get_task_threadpoolid, Int8, (Any,), task) heap_p = multiq_size(tpid) tp = tpid + 1 task.priority = priority + # TODO: task pushed to a randomly chosen thread rn = cong(heap_p, cong_unbias[tp]) tpheaps = heaps[tp] while !trylock(tpheaps[rn].lock) @@ -174,6 +178,7 @@ function multiq_deletemin() prio1 = heap.tasks[1].priority end @atomic :monotonic heap.priority = prio1 + ccall(:jl_tv_multiq_m_inc, Cvoid, ()) unlock(heap.lock) return task diff --git a/base/task.jl b/base/task.jl index d7b144150301c..1f6734db5ab1c 100644 --- a/base/task.jl +++ b/base/task.jl @@ -801,6 +801,7 @@ function schedule(t::Task, @nospecialize(arg); error=false) t.queue === nothing || Base.error("schedule: Task not runnable") setfield!(t, :result, arg) end + # TODO: how do we ensure that the same task is not enqueued multiple times? enq_work(t) return t end @@ -834,6 +835,7 @@ immediately yields to `t` before calling the scheduler. function yield(t::Task, @nospecialize(x=nothing)) (t._state === task_state_runnable && t.queue === nothing) || error("yield: Task not runnable") t.result = x + ccall(:jl_tv_tasks_running_m_inc, Cvoid, ()) enq_work(current_task()) set_next_task(t) return try_yieldto(ensure_rescheduled) @@ -855,6 +857,7 @@ function yieldto(t::Task, @nospecialize(x=nothing)) elseif t._state === task_state_failed throw(t.result) end + ccall(:jl_tv_tasks_running_m_inc, Cvoid, ()) t.result = x set_next_task(t) return try_yieldto(identity) @@ -881,6 +884,7 @@ end # yield to a task, throwing an exception in it function throwto(t::Task, @nospecialize exc) + ccall(:jl_tv_tasks_running_m_inc, Cvoid, ()) t.result = exc t._isexception = true set_next_task(t) @@ -933,10 +937,14 @@ checktaskempty = Partr.multiq_check_empty end function wait() + ccall(:jl_tv_tasks_running_m_inc, Cvoid, ()) GC.safepoint() W = workqueue_for(Threads.threadid()) poptask(W) result = try_yieldto(ensure_rescheduled) + # TODO: how does this call to process_events() interact with locks / conditions? + # First thing a task does after waking is to process events? + # Will there be contention on libuv lock? process_events() # return when we come out of the queue return result diff --git a/base/threadingconstructs.jl b/base/threadingconstructs.jl index b4c5c22c5cf8e..8812846e1d114 100644 --- a/base/threadingconstructs.jl +++ b/base/threadingconstructs.jl @@ -324,6 +324,7 @@ macro spawn(args...) let $(letargs...) local task = Task($thunk) task.sticky = false + # TODO: return value from jl_set_task_threadpoolid not checked ccall(:jl_set_task_threadpoolid, Cint, (Any, Int8), task, $tpid) if $(Expr(:islocal, var)) put!($var, task) diff --git a/src/jl_exported_funcs.inc b/src/jl_exported_funcs.inc index 081e9e42a694d..143195cbf1bc7 100644 --- a/src/jl_exported_funcs.inc +++ b/src/jl_exported_funcs.inc @@ -475,6 +475,10 @@ XX(jl_try_substrtof) \ XX(jl_tty_set_mode) \ XX(jl_tupletype_fill) \ + XX(jl_tv_multiq_p_inc) \ + XX(jl_tv_multiq_m_inc) \ + XX(jl_tv_tasks_running_m_inc) \ + XX(jl_tv_getmetric) \ XX(jl_typeassert) \ XX(jl_typeinf_begin) \ XX(jl_typeinf_end) \ diff --git a/src/julia_internal.h b/src/julia_internal.h index 814d66751c86f..5665894282728 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -18,6 +18,15 @@ #define sleep(x) Sleep(1000*x) #endif +extern JL_DLLEXPORT _Atomic(uint64_t) jl_tv_threads_waiting_p; +extern JL_DLLEXPORT _Atomic(uint64_t) jl_tv_threads_waiting_m; +extern JL_DLLEXPORT _Atomic(uint64_t) jl_tv_tasks_p; +extern JL_DLLEXPORT _Atomic(uint64_t) jl_tv_tasks_m; +extern JL_DLLEXPORT _Atomic(uint64_t) jl_tv_multiq_p; +extern JL_DLLEXPORT _Atomic(uint64_t) jl_tv_multiq_m; +extern JL_DLLEXPORT _Atomic(uint64_t) jl_tv_tasks_running_p; +extern JL_DLLEXPORT _Atomic(uint64_t) jl_tv_tasks_running_m; + #ifdef __cplusplus extern "C" { #endif diff --git a/src/partr.c b/src/partr.c index c0c2e8907db92..34e96a70d24f1 100644 --- a/src/partr.c +++ b/src/partr.c @@ -30,6 +30,7 @@ static const int16_t sleeping = 1; // invariant: Any particular thread is not asleep unless that thread's sleep_check_state is sleeping. // invariant: The transition of a thread state to sleeping must be followed by a check that there wasn't work pending for it. // information: Observing thread not-sleeping is sufficient to ensure the target thread will subsequently inspect its local queue. + // ^^^ ??? TODO // information: Observing thread is-sleeping says it may be necessary to notify it at least once to wakeup. It may already be awake however for a variety of reasons. // information: These observations require sequentially-consistent fences to be inserted between each of those operational phases. // [^store_buffering_1]: These fences are used to avoid the cycle 2b -> 1a -> 1b -> 2a -> 2b where @@ -187,6 +188,9 @@ static int sleep_check_after_threshold(uint64_t *start_cycles) *start_cycles = jl_hrtime(); return 0; } + // TODO: jl_hrtime() is a wall clock timestamp. This OS thread is not guaranteed to + // run continuously- there might be a context switch, and this thread could resume + // well after sleep_threshold has elapsed? uint64_t elapsed_cycles = jl_hrtime() - (*start_cycles); if (elapsed_cycles >= sleep_threshold) { *start_cycles = 0; @@ -195,12 +199,15 @@ static int sleep_check_after_threshold(uint64_t *start_cycles) return 0; } - +// this doesn't guarantee that on return the thread is waking or awake. +// there is a race condition here where the other thread goes to sleep just +// after this thread checks its state and sees !(jl_atomic_load_relaxed(&other->sleep_check_state) == sleeping) static int wake_thread(int16_t tid) { jl_ptls_t other = jl_all_tls_states[tid]; int8_t state = sleeping; + // TODO: use of condition variable here doesn't adhere to required discipline? if (jl_atomic_load_relaxed(&other->sleep_check_state) == sleeping) { if (jl_atomic_cmpswap_relaxed(&other->sleep_check_state, &state, not_sleeping)) { JL_PROBE_RT_SLEEP_CHECK_WAKE(other, state); @@ -255,6 +262,8 @@ JL_DLLEXPORT void jl_wakeup_thread(int16_t tid) } // check if the other threads might be sleeping if (tid == -1) { + // TODO: every thread woken up when something added to multi-queue?? + // something added to the multi-queue: notify all threads // in the future, we might want to instead wake some fraction of threads, // and let each of those wake additional threads if they find work @@ -281,7 +290,7 @@ static jl_task_t *get_next_task(jl_value_t *trypoptask, jl_value_t *q) jl_task_t *task = (jl_task_t*)jl_apply_generic(trypoptask, &q, 1); if (jl_typeis(task, jl_task_type)) { int self = jl_atomic_load_relaxed(&jl_current_task->tid); - jl_set_task_tid(task, self); + jl_set_task_tid(task, self); // TODO: return value not checked return task; } return NULL; @@ -302,6 +311,7 @@ static int may_sleep(jl_ptls_t ptls) JL_NOTSAFEPOINT return jl_atomic_load_relaxed(&ptls->sleep_check_state) == sleeping; } +// TODO: what is _threadedregion? extern _Atomic(unsigned) _threadedregion; JL_DLLEXPORT jl_task_t *jl_task_get_next(jl_value_t *trypoptask, jl_value_t *q, jl_value_t *checkempty) @@ -420,7 +430,9 @@ JL_DLLEXPORT jl_task_t *jl_task_get_next(jl_value_t *trypoptask, jl_value_t *q, int8_t gc_state = jl_gc_safe_enter(ptls); uv_mutex_lock(&sleep_locks[ptls->tid]); while (may_sleep(ptls)) { + jl_atomic_fetch_add_relaxed(&jl_tv_threads_waiting_p, 1); uv_cond_wait(&wake_signals[ptls->tid], &sleep_locks[ptls->tid]); + jl_atomic_fetch_add_relaxed(&jl_tv_threads_waiting_m, 1); // TODO: help with gc work here, if applicable } assert(jl_atomic_load_relaxed(&ptls->sleep_check_state) == not_sleeping); diff --git a/src/task.c b/src/task.c index a0577132eca8c..10e7e86b129cb 100644 --- a/src/task.c +++ b/src/task.c @@ -228,6 +228,7 @@ static _Atomic(jl_function_t*) task_done_hook_func JL_GLOBALLY_ROOTED = NULL; void JL_NORETURN jl_finish_task(jl_task_t *t) { + jl_atomic_fetch_add_relaxed(&jl_tv_tasks_m, 1); jl_task_t *ct = jl_current_task; JL_PROBE_RT_FINISH_TASK(ct); JL_SIGATOMIC_BEGIN(); @@ -518,6 +519,7 @@ static void ctx_switch(jl_task_t *lastt) JL_DLLEXPORT void jl_switch(void) { + jl_atomic_fetch_add_relaxed(&jl_tv_tasks_running_p, 1); jl_task_t *ct = jl_current_task; jl_ptls_t ptls = ct->ptls; jl_task_t *t = ptls->next_task; @@ -820,6 +822,7 @@ JL_DLLEXPORT jl_task_t *jl_new_task(jl_function_t *start, jl_value_t *completion #ifdef _COMPILER_TSAN_ENABLED_ t->ctx.tsan_state = __tsan_create_fiber(0); #endif + jl_atomic_fetch_add_relaxed(&jl_tv_tasks_p, 1); return t; } @@ -842,6 +845,7 @@ JL_DLLEXPORT jl_value_t *jl_get_root_task(void) return (jl_value_t*)ct->ptls->root_task; } +// TODO: this function has no callers? JL_DLLEXPORT void jl_task_wait() { static jl_function_t *wait_func = NULL; @@ -914,6 +918,7 @@ CFI_NORETURN jl_atomic_store_release(&pt->tid, -1); #endif + jl_atomic_fetch_add_relaxed(&jl_tv_tasks_running_p, 1); ct->started = 1; JL_PROBE_RT_START_TASK(ct); if (jl_atomic_load_relaxed(&ct->_isexception)) { @@ -939,6 +944,7 @@ CFI_NORETURN skip_pop_exception:; } ct->result = res; + jl_atomic_fetch_add_relaxed(&jl_tv_tasks_running_m, 1); jl_gc_wb(ct, ct->result); jl_finish_task(ct); jl_gc_debug_critical_error(); diff --git a/src/threading.c b/src/threading.c index 4464406d21a76..7ad5d6ffab7cc 100644 --- a/src/threading.c +++ b/src/threading.c @@ -291,6 +291,40 @@ JL_DLLEXPORT _Atomic(uint8_t) jl_measure_compile_time_enabled = 0; JL_DLLEXPORT _Atomic(uint64_t) jl_cumulative_compile_time = 0; JL_DLLEXPORT _Atomic(uint64_t) jl_cumulative_recompile_time = 0; +JL_DLLEXPORT _Atomic(uint64_t) jl_tv_threads_waiting_p = 0; +JL_DLLEXPORT _Atomic(uint64_t) jl_tv_threads_waiting_m = 0; +JL_DLLEXPORT _Atomic(uint64_t) jl_tv_tasks_p = 0; +JL_DLLEXPORT _Atomic(uint64_t) jl_tv_tasks_m = 0; +JL_DLLEXPORT _Atomic(uint64_t) jl_tv_multiq_p = 0; +JL_DLLEXPORT _Atomic(uint64_t) jl_tv_multiq_m = 0; +JL_DLLEXPORT _Atomic(uint64_t) jl_tv_tasks_running_p = 0; +JL_DLLEXPORT _Atomic(uint64_t) jl_tv_tasks_running_m = 0; + +JL_DLLEXPORT void jl_tv_multiq_p_inc(void) +{ jl_atomic_fetch_add_relaxed(&jl_tv_multiq_p, 1); } + +JL_DLLEXPORT void jl_tv_multiq_m_inc(void) +{ jl_atomic_fetch_add_relaxed(&jl_tv_multiq_m, 1); } + +JL_DLLEXPORT void jl_tv_tasks_running_m_inc(void) +{ jl_atomic_fetch_add_relaxed(&jl_tv_tasks_running_m, 1); } + +JL_DLLEXPORT int jl_tv_getmetric(int i) +{ + switch(i) + { + case 1: return jl_atomic_load_relaxed(&jl_tv_threads_waiting_p); + case 2: return jl_atomic_load_relaxed(&jl_tv_threads_waiting_m); + case 3: return jl_atomic_load_relaxed(&jl_tv_tasks_p); + case 4: return jl_atomic_load_relaxed(&jl_tv_tasks_m); + case 5: return jl_atomic_load_relaxed(&jl_tv_multiq_p); + case 6: return jl_atomic_load_relaxed(&jl_tv_multiq_m); + case 7: return jl_atomic_load_relaxed(&jl_tv_tasks_running_p); + case 8: return jl_atomic_load_relaxed(&jl_tv_tasks_running_m); + default: return 0; + } +} + // return calling thread's ID JL_DLLEXPORT int16_t jl_threadid(void) { From d0d6aabbf023dd2433574e7de436fb6f393bc313 Mon Sep 17 00:00:00 2001 From: Todd Veldhuizen Date: Tue, 25 Jul 2023 15:32:44 -0400 Subject: [PATCH 2/5] Andre lock fix; tasks waiting --- base/lock.jl | 17 +++++++---------- base/task.jl | 22 ++++++++++++++++++---- src/jl_exported_funcs.inc | 3 ++- src/julia_internal.h | 4 ++-- src/task.c | 6 +++--- src/threading.c | 31 +++++++++++++++++++++++++------ 6 files changed, 57 insertions(+), 26 deletions(-) diff --git a/base/lock.jl b/base/lock.jl index 85c35dca10530..afcc2c8d5d075 100644 --- a/base/lock.jl +++ b/base/lock.jl @@ -138,16 +138,13 @@ internal counter and return immediately. rl.reentrancy_cnt = n if n == 0x0000_00000 @atomic :monotonic rl.locked_by = nothing - if (@atomicswap :release rl.havelock = 0x00) == 0x02 - (@noinline function notifywaiters(rl) - cond_wait = rl.cond_wait - lock(cond_wait) - try - notify(cond_wait) - finally - unlock(cond_wait) - end - end)(rl) + @atomic :release rl.havelock = 0x00 + cond_wait = rl.cond_wait + lock(cond_wait) + try + notify(cond_wait, all=false) + finally + unlock(cond_wait) end return true end diff --git a/base/task.jl b/base/task.jl index 1f6734db5ab1c..73e861a1454a1 100644 --- a/base/task.jl +++ b/base/task.jl @@ -635,6 +635,7 @@ function task_done_hook(t::Task) # Clear sigatomic before waiting sigatomic_end() try + ccall(:jl_tv_tasks_waiting_m_inc, Cvoid, ()) ## Kludge- wait() # this will not return catch e # If an InterruptException happens while blocked in the event loop, try handing @@ -835,7 +836,10 @@ immediately yields to `t` before calling the scheduler. function yield(t::Task, @nospecialize(x=nothing)) (t._state === task_state_runnable && t.queue === nothing) || error("yield: Task not runnable") t.result = x - ccall(:jl_tv_tasks_running_m_inc, Cvoid, ()) + # ccall(:jl_tv_tasks_running_m_inc, Cvoid, ()) + # if hash(time()) % 1000 == 0 + # print("TASK-YIELD:\n$(sprint(Base.show_backtrace, Base.stacktrace()))\n\n\n") + # end enq_work(current_task()) set_next_task(t) return try_yieldto(ensure_rescheduled) @@ -857,7 +861,10 @@ function yieldto(t::Task, @nospecialize(x=nothing)) elseif t._state === task_state_failed throw(t.result) end - ccall(:jl_tv_tasks_running_m_inc, Cvoid, ()) + # ccall(:jl_tv_tasks_running_m_inc, Cvoid, ()) + # if hash(time()) % 1000 == 0 + # print("TASK-YIELD:\n$(sprint(Base.show_backtrace, Base.stacktrace()))\n\n\n") + # end t.result = x set_next_task(t) return try_yieldto(identity) @@ -884,7 +891,10 @@ end # yield to a task, throwing an exception in it function throwto(t::Task, @nospecialize exc) - ccall(:jl_tv_tasks_running_m_inc, Cvoid, ()) + # ccall(:jl_tv_tasks_running_m_inc, Cvoid, ()) + # if hash(time()) % 1000 == 0 + # print("TASK-YIELD:\n$(sprint(Base.show_backtrace, Base.stacktrace()))\n\n\n") + # end t.result = exc t._isexception = true set_next_task(t) @@ -937,7 +947,10 @@ checktaskempty = Partr.multiq_check_empty end function wait() - ccall(:jl_tv_tasks_running_m_inc, Cvoid, ()) + ccall(:jl_tv_tasks_waiting_p_inc, Cvoid, ()) + # if hash(time()) % 1000 == 0 + # print("TASK-YIELD:\n$(sprint(Base.show_backtrace, Base.stacktrace()))\n\n\n") + # end GC.safepoint() W = workqueue_for(Threads.threadid()) poptask(W) @@ -947,6 +960,7 @@ function wait() # Will there be contention on libuv lock? process_events() # return when we come out of the queue + ccall(:jl_tv_tasks_waiting_m_inc, Cvoid, ()) return result end diff --git a/src/jl_exported_funcs.inc b/src/jl_exported_funcs.inc index 143195cbf1bc7..3f543c9140deb 100644 --- a/src/jl_exported_funcs.inc +++ b/src/jl_exported_funcs.inc @@ -477,7 +477,8 @@ XX(jl_tupletype_fill) \ XX(jl_tv_multiq_p_inc) \ XX(jl_tv_multiq_m_inc) \ - XX(jl_tv_tasks_running_m_inc) \ + XX(jl_tv_tasks_waiting_p_inc) \ + XX(jl_tv_tasks_waiting_m_inc) \ XX(jl_tv_getmetric) \ XX(jl_typeassert) \ XX(jl_typeinf_begin) \ diff --git a/src/julia_internal.h b/src/julia_internal.h index 5665894282728..5877b7d1c2e81 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -24,8 +24,8 @@ extern JL_DLLEXPORT _Atomic(uint64_t) jl_tv_tasks_p; extern JL_DLLEXPORT _Atomic(uint64_t) jl_tv_tasks_m; extern JL_DLLEXPORT _Atomic(uint64_t) jl_tv_multiq_p; extern JL_DLLEXPORT _Atomic(uint64_t) jl_tv_multiq_m; -extern JL_DLLEXPORT _Atomic(uint64_t) jl_tv_tasks_running_p; -extern JL_DLLEXPORT _Atomic(uint64_t) jl_tv_tasks_running_m; +extern JL_DLLEXPORT _Atomic(uint64_t) jl_tv_tasks_waiting_p; +extern JL_DLLEXPORT _Atomic(uint64_t) jl_tv_tasks_waiting_m; #ifdef __cplusplus extern "C" { diff --git a/src/task.c b/src/task.c index 10e7e86b129cb..fbcb42f3c469c 100644 --- a/src/task.c +++ b/src/task.c @@ -519,7 +519,7 @@ static void ctx_switch(jl_task_t *lastt) JL_DLLEXPORT void jl_switch(void) { - jl_atomic_fetch_add_relaxed(&jl_tv_tasks_running_p, 1); + //jl_atomic_fetch_add_relaxed(&jl_tv_tasks_running_p, 1); jl_task_t *ct = jl_current_task; jl_ptls_t ptls = ct->ptls; jl_task_t *t = ptls->next_task; @@ -918,7 +918,7 @@ CFI_NORETURN jl_atomic_store_release(&pt->tid, -1); #endif - jl_atomic_fetch_add_relaxed(&jl_tv_tasks_running_p, 1); + //jl_atomic_fetch_add_relaxed(&jl_tv_tasks_running_p, 1); ct->started = 1; JL_PROBE_RT_START_TASK(ct); if (jl_atomic_load_relaxed(&ct->_isexception)) { @@ -944,7 +944,7 @@ CFI_NORETURN skip_pop_exception:; } ct->result = res; - jl_atomic_fetch_add_relaxed(&jl_tv_tasks_running_m, 1); + //jl_atomic_fetch_add_relaxed(&jl_tv_tasks_running_m, 1); jl_gc_wb(ct, ct->result); jl_finish_task(ct); jl_gc_debug_critical_error(); diff --git a/src/threading.c b/src/threading.c index 7ad5d6ffab7cc..ec189ab994a67 100644 --- a/src/threading.c +++ b/src/threading.c @@ -297,8 +297,8 @@ JL_DLLEXPORT _Atomic(uint64_t) jl_tv_tasks_p = 0; JL_DLLEXPORT _Atomic(uint64_t) jl_tv_tasks_m = 0; JL_DLLEXPORT _Atomic(uint64_t) jl_tv_multiq_p = 0; JL_DLLEXPORT _Atomic(uint64_t) jl_tv_multiq_m = 0; -JL_DLLEXPORT _Atomic(uint64_t) jl_tv_tasks_running_p = 0; -JL_DLLEXPORT _Atomic(uint64_t) jl_tv_tasks_running_m = 0; +JL_DLLEXPORT _Atomic(uint64_t) jl_tv_tasks_waiting_p = 0; +JL_DLLEXPORT _Atomic(uint64_t) jl_tv_tasks_waiting_m = 0; JL_DLLEXPORT void jl_tv_multiq_p_inc(void) { jl_atomic_fetch_add_relaxed(&jl_tv_multiq_p, 1); } @@ -306,8 +306,27 @@ JL_DLLEXPORT void jl_tv_multiq_p_inc(void) JL_DLLEXPORT void jl_tv_multiq_m_inc(void) { jl_atomic_fetch_add_relaxed(&jl_tv_multiq_m, 1); } -JL_DLLEXPORT void jl_tv_tasks_running_m_inc(void) -{ jl_atomic_fetch_add_relaxed(&jl_tv_tasks_running_m, 1); } +JL_DLLEXPORT _Atomic(uint64_t) jl_tv_dbg_counter = 0; + +JL_DLLEXPORT void jl_tv_tasks_waiting_p_inc(void) +{ + jl_atomic_fetch_add_relaxed(&jl_tv_tasks_waiting_p, 1); + // if (jl_atomic_fetch_add_relaxed(&jl_tv_dbg_counter, 1) % 521 == 257) + // { + // JL_TRY { + // jl_error(""); // get a backtrace + // } + // JL_CATCH { + // jl_printf((JL_STREAM*)STDERR_FILENO, "\n\nSample of task waiting:\n"); + // jlbacktrace(); // written to STDERR_FILENO + // } + // } +} + +JL_DLLEXPORT void jl_tv_tasks_waiting_m_inc(void) +{ + jl_atomic_fetch_add_relaxed(&jl_tv_tasks_waiting_m, 1); +} JL_DLLEXPORT int jl_tv_getmetric(int i) { @@ -319,8 +338,8 @@ JL_DLLEXPORT int jl_tv_getmetric(int i) case 4: return jl_atomic_load_relaxed(&jl_tv_tasks_m); case 5: return jl_atomic_load_relaxed(&jl_tv_multiq_p); case 6: return jl_atomic_load_relaxed(&jl_tv_multiq_m); - case 7: return jl_atomic_load_relaxed(&jl_tv_tasks_running_p); - case 8: return jl_atomic_load_relaxed(&jl_tv_tasks_running_m); + case 7: return jl_atomic_load_relaxed(&jl_tv_tasks_waiting_p); + case 8: return jl_atomic_load_relaxed(&jl_tv_tasks_waiting_m); default: return 0; } } From dedf77eb365b33df9532551145a656ee49f1ebb2 Mon Sep 17 00:00:00 2001 From: Todd Veldhuizen Date: Thu, 27 Jul 2023 09:23:37 -0400 Subject: [PATCH 3/5] . --- src/threading.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/threading.c b/src/threading.c index ec189ab994a67..b54d6934ba673 100644 --- a/src/threading.c +++ b/src/threading.c @@ -311,16 +311,16 @@ JL_DLLEXPORT _Atomic(uint64_t) jl_tv_dbg_counter = 0; JL_DLLEXPORT void jl_tv_tasks_waiting_p_inc(void) { jl_atomic_fetch_add_relaxed(&jl_tv_tasks_waiting_p, 1); - // if (jl_atomic_fetch_add_relaxed(&jl_tv_dbg_counter, 1) % 521 == 257) - // { - // JL_TRY { - // jl_error(""); // get a backtrace - // } - // JL_CATCH { - // jl_printf((JL_STREAM*)STDERR_FILENO, "\n\nSample of task waiting:\n"); - // jlbacktrace(); // written to STDERR_FILENO - // } - // } + if (jl_atomic_fetch_add_relaxed(&jl_tv_dbg_counter, 1) % 521 == 257) + { + JL_TRY { + jl_error(""); // get a backtrace + } + JL_CATCH { + jl_printf((JL_STREAM*)STDERR_FILENO, "\n\nSample of task waiting:\n"); + jlbacktrace(); // written to STDERR_FILENO + } + } } JL_DLLEXPORT void jl_tv_tasks_waiting_m_inc(void) From 1060a5a07c53b45437c221bd7d0913f0785dd622 Mon Sep 17 00:00:00 2001 From: Todd Veldhuizen Date: Wed, 2 Aug 2023 11:48:43 -0400 Subject: [PATCH 4/5] unbiased profile --- src/signals-unix.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/signals-unix.c b/src/signals-unix.c index a9a7dec745543..629b38161a983 100644 --- a/src/signals-unix.c +++ b/src/signals-unix.c @@ -876,6 +876,12 @@ static void *signal_listener(void *arg) // notify thread to resume jl_thread_resume(i, sig); + + if (!critical) + { + // Kludge: only sample a single thread, to get an unbiased sample + break; + } } jl_unlock_profile(); } From c449161ccf0864584fc4a265da87b796eb8733d1 Mon Sep 17 00:00:00 2001 From: Todd Veldhuizen Date: Thu, 10 Aug 2023 19:10:12 -0400 Subject: [PATCH 5/5] . --- src/partr.pdf | Bin 0 -> 22040 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/partr.pdf diff --git a/src/partr.pdf b/src/partr.pdf new file mode 100644 index 0000000000000000000000000000000000000000..5476ccd3c6c0e68a770627408465558f1ed18da6 GIT binary patch literal 22040 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 Lh{#EO4aWZfl!M%F literal 0 HcmV?d00001