@@ -2814,13 +2814,16 @@ JL_EXTENSION NOINLINE void gc_mark_loop_serial(jl_ptls_t ptls)
2814
2814
gc_drain_own_chunkqueue (ptls , & ptls -> mark_queue );
2815
2815
}
2816
2816
2817
- void gc_mark_and_steal (jl_ptls_t ptls )
2817
+ int gc_mark_and_steal (jl_ptls_t ptls )
2818
2818
{
2819
2819
jl_gc_markqueue_t * mq = & ptls -> mark_queue ;
2820
2820
jl_gc_markqueue_t * mq_master = NULL ;
2821
2821
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 ;
2824
2827
void * new_obj ;
2825
2828
jl_gc_chunk_t c ;
2826
2829
pop : {
@@ -2836,6 +2839,7 @@ void gc_mark_and_steal(jl_ptls_t ptls)
2836
2839
goto steal ;
2837
2840
}
2838
2841
mark : {
2842
+ marked = 1 ;
2839
2843
gc_mark_outrefs (ptls , mq , new_obj , 0 );
2840
2844
goto pop ;
2841
2845
}
@@ -2864,12 +2868,10 @@ void gc_mark_and_steal(jl_ptls_t ptls)
2864
2868
}
2865
2869
}
2866
2870
// 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 ;
2873
2875
}
2874
2876
// Try to steal pointer from random GC thread
2875
2877
for (int i = 0 ; i < 4 * jl_n_markthreads ; i ++ ) {
@@ -2886,37 +2888,141 @@ void gc_mark_and_steal(jl_ptls_t ptls)
2886
2888
if (new_obj != NULL )
2887
2889
goto mark ;
2888
2890
}
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 ++ ;
2894
2926
}
2895
2927
}
2928
+ else {
2929
+ // sleep for 1ms
2930
+ uv_sleep (1 );
2931
+ }
2896
2932
}
2897
2933
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 )
2899
2978
{
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 ) {
2904
3014
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
+ }
2907
3018
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 );
2916
3025
}
2917
- jl_atomic_fetch_add (& gc_n_threads_marking , -1 );
2918
- // Failed to steal
2919
- gc_backoff (& backoff );
2920
3026
}
2921
3027
}
2922
3028
@@ -2926,7 +3032,7 @@ void gc_mark_loop(jl_ptls_t ptls)
2926
3032
gc_mark_loop_serial (ptls );
2927
3033
}
2928
3034
else {
2929
- gc_mark_loop_parallel (ptls , 1 );
3035
+ gc_mark_loop_master (ptls );
2930
3036
}
2931
3037
}
2932
3038
0 commit comments