@@ -2742,13 +2742,16 @@ JL_EXTENSION NOINLINE void gc_mark_loop_serial(jl_ptls_t ptls)
27422742 gc_drain_own_chunkqueue (ptls , & ptls -> mark_queue );
27432743}
27442744
2745- void gc_mark_and_steal (jl_ptls_t ptls )
2745+ int gc_mark_and_steal (jl_ptls_t ptls )
27462746{
27472747 jl_gc_markqueue_t * mq = & ptls -> mark_queue ;
27482748 jl_gc_markqueue_t * mq_master = NULL ;
27492749 int master_tid = jl_atomic_load (& gc_master_tid );
2750- if (master_tid != -1 )
2751- mq_master = & gc_all_tls_states [master_tid ]-> mark_queue ;
2750+ if (master_tid == -1 ) {
2751+ return 0 ;
2752+ }
2753+ mq_master = & gc_all_tls_states [master_tid ]-> mark_queue ;
2754+ int marked = 0 ;
27522755 void * new_obj ;
27532756 jl_gc_chunk_t c ;
27542757 pop : {
@@ -2764,6 +2767,7 @@ void gc_mark_and_steal(jl_ptls_t ptls)
27642767 goto steal ;
27652768 }
27662769 mark : {
2770+ marked = 1 ;
27672771 gc_mark_outrefs (ptls , mq , new_obj , 0 );
27682772 goto pop ;
27692773 }
@@ -2792,12 +2796,10 @@ void gc_mark_and_steal(jl_ptls_t ptls)
27922796 }
27932797 }
27942798 // Try to steal chunk from master thread
2795- if (mq_master != NULL ) {
2796- c = gc_chunkqueue_steal_from (mq_master );
2797- if (c .cid != GC_empty_chunk ) {
2798- gc_mark_chunk (ptls , mq , & c );
2799- goto pop ;
2800- }
2799+ c = gc_chunkqueue_steal_from (mq_master );
2800+ if (c .cid != GC_empty_chunk ) {
2801+ gc_mark_chunk (ptls , mq , & c );
2802+ goto pop ;
28012803 }
28022804 // Try to steal pointer from random GC thread
28032805 for (int i = 0 ; i < 4 * jl_n_markthreads ; i ++ ) {
@@ -2814,37 +2816,98 @@ void gc_mark_and_steal(jl_ptls_t ptls)
28142816 if (new_obj != NULL )
28152817 goto mark ;
28162818 }
2817- // Try to steal pointer from master thread
2818- if (mq_master != NULL ) {
2819- new_obj = gc_ptr_queue_steal_from (mq_master );
2820- if (new_obj != NULL )
2821- goto mark ;
2822- }
2819+ new_obj = gc_ptr_queue_steal_from (mq_master );
2820+ if (new_obj != NULL )
2821+ goto mark ;
28232822 }
2823+ return marked ;
28242824}
28252825
2826- void gc_mark_loop_parallel (jl_ptls_t ptls , int master )
2826+ #define GC_BACKOFF_MIN_LG2 (1 << 3)
2827+ #define GC_BACKOFF_MAX_LG2 (1 << 11)
2828+
2829+ STATIC_INLINE void gc_sched_yield_reset_state (gc_sched_state_t * s ) JL_NOTSAFEPOINT
28272830{
2828- int backoff = GC_BACKOFF_MIN ;
2829- if (master ) {
2830- jl_atomic_store (& gc_master_tid , ptls -> tid );
2831- // Wake threads up and try to do some work
2832- uv_mutex_lock (& gc_threads_lock );
2833- jl_atomic_fetch_add (& gc_n_threads_marking , 1 );
2834- uv_cond_broadcast (& gc_threads_cond );
2835- uv_mutex_unlock (& gc_threads_lock );
2836- gc_mark_and_steal (ptls );
2837- jl_atomic_fetch_add (& gc_n_threads_marking , -1 );
2831+ s -> yield_phase = GC_SPINNING ;
2832+ s -> backoff_lg2 = GC_BACKOFF_MIN_LG2 ;
2833+ s -> n_spins_at_max = 0 ;
2834+ }
2835+
2836+ STATIC_INLINE void gc_sched_yield (gc_sched_state_t * s ) JL_NOTSAFEPOINT
2837+ {
2838+ if (s -> yield_phase == GC_SPINNING ) {
2839+ // spin for 2^backoff_lg2 iterations
2840+ for (int i = 0 ; i < (1 << s -> backoff_lg2 ); i ++ ) {
2841+ jl_cpu_pause ();
2842+ }
2843+ if (s -> backoff_lg2 == GC_BACKOFF_MAX_LG2 ) {
2844+ s -> n_spins_at_max ++ ;
2845+ // has been spinning for a while... should
2846+ // just sleep in the next failed steal attempt
2847+ if (s -> n_spins_at_max >= 4 ) {
2848+ s -> yield_phase = GC_SLEEPING ;
2849+ }
2850+ }
2851+ else {
2852+ s -> backoff_lg2 ++ ;
2853+ }
28382854 }
2855+ else {
2856+ // sleep for 1ms
2857+ uv_sleep (1 );
2858+ }
2859+ }
2860+
2861+ void gc_mark_loop_master_init (jl_ptls_t ptls )
2862+ {
2863+ jl_atomic_store (& gc_master_tid , ptls -> tid );
2864+ // Wake threads up and try to do some work
2865+ uv_mutex_lock (& gc_threads_lock );
2866+ jl_atomic_fetch_add (& gc_n_threads_marking , 1 );
2867+ uv_cond_broadcast (& gc_threads_cond );
2868+ uv_mutex_unlock (& gc_threads_lock );
2869+ gc_mark_and_steal (ptls );
2870+ jl_atomic_fetch_add (& gc_n_threads_marking , -1 );
2871+ }
2872+
2873+ void gc_mark_loop_parallel (jl_ptls_t ptls )
2874+ {
2875+ gc_sched_state_t s ;
2876+ gc_sched_yield_reset_state (& s );
28392877 while (jl_atomic_load (& gc_n_threads_marking ) > 0 ) {
28402878 // Try to become a thief while other threads are marking
28412879 jl_atomic_fetch_add (& gc_n_threads_marking , 1 );
2842- if (jl_atomic_load (& gc_master_tid ) != -1 ) {
2843- gc_mark_and_steal (ptls );
2844- }
2880+ int marked = gc_mark_and_steal (ptls );
28452881 jl_atomic_fetch_add (& gc_n_threads_marking , -1 );
2846- // Failed to steal
2847- gc_backoff (& backoff );
2882+ if (marked ) {
2883+ gc_sched_yield_reset_state (& s );
2884+ }
2885+ else {
2886+ gc_sched_yield (& s );
2887+ }
2888+ }
2889+ }
2890+
2891+ void gc_mark_loop_master (jl_ptls_t ptls )
2892+ {
2893+ gc_mark_loop_master_init (ptls );
2894+ gc_mark_loop_parallel (ptls );
2895+ }
2896+
2897+ STATIC_INLINE int gc_may_mark (void ) JL_NOTSAFEPOINT
2898+ {
2899+ return jl_atomic_load (& gc_n_threads_marking ) > 0 ;
2900+ }
2901+
2902+ void gc_mark_loop_worker (jl_ptls_t ptls )
2903+ {
2904+ while (1 ) {
2905+ uv_mutex_lock (& gc_threads_lock );
2906+ while (!gc_may_mark ()) {
2907+ uv_cond_wait (& gc_threads_cond , & gc_threads_lock );
2908+ }
2909+ uv_mutex_unlock (& gc_threads_lock );
2910+ gc_mark_loop_parallel (ptls );
28482911 }
28492912}
28502913
@@ -2854,7 +2917,7 @@ void gc_mark_loop(jl_ptls_t ptls)
28542917 gc_mark_loop_serial (ptls );
28552918 }
28562919 else {
2857- gc_mark_loop_parallel (ptls , 1 );
2920+ gc_mark_loop_master (ptls );
28582921 }
28592922}
28602923
0 commit comments