From 96692f5d431a3b28241228daf08c6ffd436c2582 Mon Sep 17 00:00:00 2001 From: Pete Vilter Date: Thu, 26 Jan 2023 11:28:46 -0500 Subject: [PATCH] basic task cpu time tracking --- src/jltypes.c | 10 ++++++---- src/julia.h | 4 ++++ src/task.c | 10 ++++++++++ test/threads_exec.jl | 9 +++++++++ 4 files changed, 29 insertions(+), 4 deletions(-) diff --git a/src/jltypes.c b/src/jltypes.c index a3afdc740907a..b921474689774 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -2626,7 +2626,7 @@ void jl_init_types(void) JL_GC_DISABLED NULL, jl_any_type, jl_emptysvec, - jl_perm_symsvec(15, + jl_perm_symsvec(16, "next", "queue", "storage", @@ -2641,8 +2641,9 @@ void jl_init_types(void) JL_GC_DISABLED "_state", "sticky", "_isexception", - "priority"), - jl_svec(15, + "priority", + "cpu_time_ns"), + jl_svec(16, jl_any_type, jl_any_type, jl_any_type, @@ -2657,7 +2658,8 @@ void jl_init_types(void) JL_GC_DISABLED jl_uint8_type, jl_bool_type, jl_bool_type, - jl_uint16_type), + jl_uint16_type, + jl_uint64_type), jl_emptysvec, 0, 1, 6); jl_value_t *listt = jl_new_struct(jl_uniontype_type, jl_task_type, jl_nothing_type); diff --git a/src/julia.h b/src/julia.h index 83b3f5f925fb6..adda809528492 100644 --- a/src/julia.h +++ b/src/julia.h @@ -1890,8 +1890,12 @@ typedef struct _jl_task_t { _Atomic(uint8_t) _isexception; // set if `result` is an exception to throw or that we exited with // multiqueue priority uint16_t priority; + // TODO: int32 of ms instead? + uint64_t cpu_time_ns; // time this task has spent running; updated when it yields // hidden state: + // timestamp this task was last scheduled (TODO: int32 of ms instead?) + uint64_t last_scheduled_ns; // id of owning thread - does not need to be defined until the task runs _Atomic(int16_t) tid; // threadpool id diff --git a/src/task.c b/src/task.c index a0577132eca8c..88a71e7583c5c 100644 --- a/src/task.c +++ b/src/task.c @@ -230,6 +230,7 @@ void JL_NORETURN jl_finish_task(jl_task_t *t) { jl_task_t *ct = jl_current_task; JL_PROBE_RT_FINISH_TASK(ct); + ct->cpu_time_ns += jl_hrtime() - ct->last_scheduled_ns; JL_SIGATOMIC_BEGIN(); if (jl_atomic_load_relaxed(&t->_isexception)) jl_atomic_store_release(&t->_state, JL_TASK_STATE_FAILED); @@ -534,6 +535,7 @@ JL_DLLEXPORT void jl_switch(void) jl_error("cannot switch to task running on another thread"); JL_PROBE_RT_PAUSE_TASK(ct); + ct->cpu_time_ns += jl_hrtime() - ct->last_scheduled_ns; // Store old values on the stack and reset sig_atomic_t defer_signal = ptls->defer_signal; @@ -584,6 +586,9 @@ JL_DLLEXPORT void jl_switch(void) jl_sigint_safepoint(ptls); JL_PROBE_RT_RUN_TASK(ct); + ct->last_scheduled_ns = jl_hrtime(); + + jl_gc_unsafe_leave(ptls, gc_state); } JL_DLLEXPORT void jl_switchto(jl_task_t **pt) @@ -803,6 +808,8 @@ JL_DLLEXPORT jl_task_t *jl_new_task(jl_function_t *start, jl_value_t *completion t->threadpoolid = ct->threadpoolid; t->ptls = NULL; t->world_age = ct->world_age; + t->last_scheduled_ns = 0; + t->cpu_time_ns = 0; #ifdef COPY_STACKS if (!t->copy_stack) { @@ -916,6 +923,7 @@ CFI_NORETURN ct->started = 1; JL_PROBE_RT_START_TASK(ct); + ct->last_scheduled_ns = jl_hrtime(); if (jl_atomic_load_relaxed(&ct->_isexception)) { record_backtrace(ptls, 0); jl_push_excstack(&ct->excstack, ct->result, @@ -1366,6 +1374,8 @@ jl_task_t *jl_init_root_task(jl_ptls_t ptls, void *stack_lo, void *stack_hi) ct->sticky = 1; ct->ptls = ptls; ct->world_age = 1; // OK to run Julia code on this task + ct->last_scheduled_ns = 0; + ct->cpu_time_ns = 0; ptls->root_task = ct; jl_atomic_store_relaxed(&ptls->current_task, ct); JL_GC_PROMISE_ROOTED(ct); diff --git a/test/threads_exec.jl b/test/threads_exec.jl index 9cd5992d90a74..f4e583fdf6e20 100644 --- a/test/threads_exec.jl +++ b/test/threads_exec.jl @@ -3,6 +3,7 @@ using Test using Base.Threads using Base.Threads: SpinLock +using LinearAlgebra: peakflops # for cfunction_closure include("testenv.jl") @@ -1062,3 +1063,11 @@ end popfirst!(LOAD_PATH) end end + +@testset "CPU time counter" begin + t = Threads.@spawn begin + peakflops() + end + wait(t) + @test t.cpu_time_ns > 0 +end