@@ -2814,13 +2814,16 @@ JL_EXTENSION NOINLINE void gc_mark_loop_serial(jl_ptls_t ptls)
28142814 gc_drain_own_chunkqueue (ptls , & ptls -> mark_queue );
28152815}
28162816
2817- void gc_mark_and_steal (jl_ptls_t ptls )
2817+ int gc_mark_and_steal (jl_ptls_t ptls )
28182818{
28192819 jl_gc_markqueue_t * mq = & ptls -> mark_queue ;
28202820 jl_gc_markqueue_t * mq_master = NULL ;
28212821 int master_tid = jl_atomic_load (& gc_master_tid );
2822- if (master_tid != -1 )
2823- mq_master = & gc_all_tls_states [master_tid ]-> mark_queue ;
2822+ if (master_tid == -1 ) {
2823+ return 0 ;
2824+ }
2825+ mq_master = & gc_all_tls_states [master_tid ]-> mark_queue ;
2826+ int marked = 0 ;
28242827 void * new_obj ;
28252828 jl_gc_chunk_t c ;
28262829 pop : {
@@ -2836,6 +2839,7 @@ void gc_mark_and_steal(jl_ptls_t ptls)
28362839 goto steal ;
28372840 }
28382841 mark : {
2842+ marked = 1 ;
28392843 gc_mark_outrefs (ptls , mq , new_obj , 0 );
28402844 goto pop ;
28412845 }
@@ -2864,12 +2868,10 @@ void gc_mark_and_steal(jl_ptls_t ptls)
28642868 }
28652869 }
28662870 // Try to steal chunk from master thread
2867- if (mq_master != NULL ) {
2868- c = gc_chunkqueue_steal_from (mq_master );
2869- if (c .cid != GC_empty_chunk ) {
2870- gc_mark_chunk (ptls , mq , & c );
2871- goto pop ;
2872- }
2871+ c = gc_chunkqueue_steal_from (mq_master );
2872+ if (c .cid != GC_empty_chunk ) {
2873+ gc_mark_chunk (ptls , mq , & c );
2874+ goto pop ;
28732875 }
28742876 // Try to steal pointer from random GC thread
28752877 for (int i = 0 ; i < 4 * jl_n_markthreads ; i ++ ) {
@@ -2886,37 +2888,141 @@ void gc_mark_and_steal(jl_ptls_t ptls)
28862888 if (new_obj != NULL )
28872889 goto mark ;
28882890 }
2889- // Try to steal pointer from master thread
2890- if (mq_master != NULL ) {
2891- new_obj = gc_ptr_queue_steal_from (mq_master );
2892- if (new_obj != NULL )
2893- goto mark ;
2891+ new_obj = gc_ptr_queue_steal_from (mq_master );
2892+ if (new_obj != NULL )
2893+ goto mark ;
2894+ }
2895+ return marked ;
2896+ }
2897+
2898+ #define GC_BACKOFF_MIN_LG2 (10)
2899+ #define GC_BACKOFF_MAX_LG2 (18)
2900+
2901+ STATIC_INLINE void gc_backoff_reset_state (gc_backoff_state_t * s ) JL_NOTSAFEPOINT
2902+ {
2903+ s -> backoff_phase = GC_SPINNING ;
2904+ s -> backoff_lg2 = GC_BACKOFF_MIN_LG2 ;
2905+ s -> n_spins_at_max = 0 ;
2906+ }
2907+
2908+ STATIC_INLINE void gc_backoff (gc_backoff_state_t * s ) JL_NOTSAFEPOINT
2909+ {
2910+ if (s -> backoff_phase == GC_SPINNING ) {
2911+ // spin for 2^backoff_lg2 cycles
2912+ uint64_t c0 = cycleclock ();
2913+ do {
2914+ jl_cpu_pause ();
2915+ } while (cycleclock () - c0 < (1 << s -> backoff_lg2 ));
2916+ if (s -> backoff_lg2 == GC_BACKOFF_MAX_LG2 ) {
2917+ s -> n_spins_at_max ++ ;
2918+ // has been spinning for a while... should
2919+ // just sleep in the next failed steal attempt
2920+ if (s -> n_spins_at_max >= 8 ) {
2921+ s -> backoff_phase = GC_SLEEPING ;
2922+ }
2923+ }
2924+ else {
2925+ s -> backoff_lg2 ++ ;
28942926 }
28952927 }
2928+ else {
2929+ // sleep for 1ms
2930+ uv_sleep (1 );
2931+ }
28962932}
28972933
2898- void gc_mark_loop_parallel (jl_ptls_t ptls , int master )
2934+ int gc_some_work_left_in_queue (jl_ptls_t ptls ) JL_NOTSAFEPOINT
2935+ {
2936+ if (jl_atomic_load_relaxed (& ptls -> mark_queue .ptr_queue .bottom ) !=
2937+ jl_atomic_load_relaxed (& ptls -> mark_queue .ptr_queue .top )) {
2938+ return 1 ;
2939+ }
2940+ if (jl_atomic_load_relaxed (& ptls -> mark_queue .chunk_queue .bottom ) !=
2941+ jl_atomic_load_relaxed (& ptls -> mark_queue .chunk_queue .top )) {
2942+ return 1 ;
2943+ }
2944+ return 0 ;
2945+ }
2946+
2947+ int gc_some_work_left (void ) JL_NOTSAFEPOINT
2948+ {
2949+ for (int i = gc_first_tid ; i < gc_first_tid + jl_n_markthreads ; i ++ ) {
2950+ jl_ptls_t ptls2 = gc_all_tls_states [i ];
2951+ if (gc_some_work_left_in_queue (ptls2 )) {
2952+ return 1 ;
2953+ }
2954+ }
2955+ int master_tid = jl_atomic_load (& gc_master_tid );
2956+ if (master_tid != -1 ) {
2957+ jl_ptls_t ptls2 = gc_all_tls_states [master_tid ];
2958+ if (gc_some_work_left_in_queue (ptls2 )) {
2959+ return 1 ;
2960+ }
2961+ }
2962+ return 0 ;
2963+ }
2964+
2965+ void gc_mark_loop_master_init (jl_ptls_t ptls )
2966+ {
2967+ jl_atomic_store (& gc_master_tid , ptls -> tid );
2968+ // Wake threads up and try to do some work
2969+ uv_mutex_lock (& gc_threads_lock );
2970+ jl_atomic_fetch_add (& gc_n_threads_marking , 1 );
2971+ uv_cond_broadcast (& gc_threads_cond );
2972+ uv_mutex_unlock (& gc_threads_lock );
2973+ gc_mark_and_steal (ptls );
2974+ jl_atomic_fetch_add (& gc_n_threads_marking , -1 );
2975+ }
2976+
2977+ void gc_mark_loop_parallel (jl_ptls_t ptls )
28992978{
2900- int backoff = GC_BACKOFF_MIN ;
2901- if (master ) {
2902- jl_atomic_store (& gc_master_tid , ptls -> tid );
2903- // Wake threads up and try to do some work
2979+ gc_backoff_state_t s ;
2980+ gc_backoff_reset_state (& s );
2981+ while (jl_atomic_load (& gc_n_threads_marking ) > 0 ) {
2982+ if (gc_some_work_left ()) {
2983+ // Try to become a thief while other threads are marking
2984+ jl_atomic_fetch_add (& gc_n_threads_marking , 1 );
2985+ int marked = gc_mark_and_steal (ptls );
2986+ jl_atomic_fetch_add (& gc_n_threads_marking , -1 );
2987+ if (marked ) {
2988+ gc_backoff_reset_state (& s );
2989+ }
2990+ }
2991+ gc_backoff (& s );
2992+ }
2993+ }
2994+
2995+ void gc_mark_loop_master (jl_ptls_t ptls )
2996+ {
2997+ gc_mark_loop_master_init (ptls );
2998+ gc_mark_loop_parallel (ptls );
2999+ }
3000+
3001+ STATIC_INLINE int gc_may_mark (void ) JL_NOTSAFEPOINT
3002+ {
3003+ return jl_atomic_load (& gc_n_threads_marking ) > 0 ;
3004+ }
3005+
3006+ STATIC_INLINE int gc_may_sweep (jl_ptls_t ptls ) JL_NOTSAFEPOINT
3007+ {
3008+ return jl_atomic_load (& ptls -> gc_sweeps_requested ) > 0 ;
3009+ }
3010+
3011+ void gc_worker_loop (jl_ptls_t ptls )
3012+ {
3013+ while (1 ) {
29043014 uv_mutex_lock (& gc_threads_lock );
2905- jl_atomic_fetch_add (& gc_n_threads_marking , 1 );
2906- uv_cond_broadcast (& gc_threads_cond );
3015+ while (!gc_may_mark () && !gc_may_sweep (ptls )) {
3016+ uv_cond_wait (& gc_threads_cond , & gc_threads_lock );
3017+ }
29073018 uv_mutex_unlock (& gc_threads_lock );
2908- gc_mark_and_steal (ptls );
2909- jl_atomic_fetch_add (& gc_n_threads_marking , -1 );
2910- }
2911- while (jl_atomic_load (& gc_n_threads_marking ) > 0 ) {
2912- // Try to become a thief while other threads are marking
2913- jl_atomic_fetch_add (& gc_n_threads_marking , 1 );
2914- if (jl_atomic_load (& gc_master_tid ) != -1 ) {
2915- gc_mark_and_steal (ptls );
3019+ if (gc_may_mark ()) {
3020+ gc_mark_loop_parallel (ptls );
3021+ }
3022+ if (gc_may_sweep (ptls )) { // not an else!
3023+ gc_sweep_pool_parallel ();
3024+ jl_atomic_fetch_add (& ptls -> gc_sweeps_requested , -1 );
29163025 }
2917- jl_atomic_fetch_add (& gc_n_threads_marking , -1 );
2918- // Failed to steal
2919- gc_backoff (& backoff );
29203026 }
29213027}
29223028
@@ -2926,7 +3032,7 @@ void gc_mark_loop(jl_ptls_t ptls)
29263032 gc_mark_loop_serial (ptls );
29273033 }
29283034 else {
2929- gc_mark_loop_parallel (ptls , 1 );
3035+ gc_mark_loop_master (ptls );
29303036 }
29313037}
29323038
0 commit comments