@@ -698,11 +698,16 @@ static int heaps_used = 0;
698698/* Too large a heap size and you can never free a page, due to fragmentation. Too
699699 small, and you have too many heaps and get stack errors. */
700700static int heap_size = 32768 ;
701+ static int heap_increase_rate = 4 ;
702+
701703static int eden_heaps = 24 ;
704+ static int eden_preemptive_heaps = 4 ;
705+ static int eden_preemptive_total_free_slots ;
702706
703707typedef struct heaps_space {
704- int heap_slots_total ;
705708 int num_heaps ;
709+ unsigned int total_slots ;
710+ unsigned int total_free_slots ;
706711 enum lifetime lifetime ;
707712 RVALUE * freelist ;
708713} heaps_space_t ;
@@ -781,7 +786,11 @@ static void set_gc_parameters()
781786#endif
782787
783788 SET_INT_ENV_VAR ("RUBY_GC_HEAP_SIZE" , heap_size )
789+ SET_INT_ENV_VAR ("RUBY_GC_HEAP_INCREASE_RATE" , heap_increase_rate )
784790 SET_INT_ENV_VAR ("RUBY_GC_EDEN_HEAPS" , eden_heaps )
791+ SET_INT_ENV_VAR ("RUBY_GC_EDEN_PREEMPTIVE_HEAPS" , eden_preemptive_heaps )
792+
793+ eden_preemptive_total_free_slots = eden_preemptive_heaps * heap_size ;
785794
786795 WITH_FLOAT_ENV_VAR ("RUBY_GC_LONGLIFE_LAZINESS" , longlife_laziness )
787796 if (val >= 1 ) {
@@ -895,7 +904,8 @@ add_heap(heaps_space_t *heaps_space)
895904 if (himem < pend ) {
896905 himem = pend ;
897906 }
898- heaps_space -> heap_slots_total += new_heap_size ;
907+ heaps_space -> total_slots += new_heap_size ;
908+ heaps_space -> total_free_slots += new_heap_size ;
899909 heaps_space -> num_heaps ++ ;
900910 heaps_used ++ ;
901911
@@ -921,6 +931,7 @@ pop_freelist(heaps_space_t* heaps_space)
921931{
922932 VALUE obj = (VALUE )heaps_space -> freelist ;
923933 heaps_space -> freelist = heaps_space -> freelist -> as .free .next ;
934+ heaps_space -> total_free_slots -- ;
924935 RANY (obj )-> as .free .next = 0 ;
925936#ifdef GC_DEBUG
926937 MEMZERO ((void * )obj , RVALUE , 1 );
@@ -1763,18 +1774,25 @@ static int obj_free _((VALUE));
17631774static void add_to_correct_freelist (RVALUE * p )
17641775{
17651776 int longlived = OBJ_LONGLIVED (p );
1777+ heaps_space_t * heaps_space ;
1778+
17661779 // Has explicit longlife flag
17671780 if (longlived ) {
1768- push_freelist ( & longlife_heaps_space , p ) ;
1781+ heaps_space = & longlife_heaps_space ;
17691782 }
17701783 // Has some flags (so they weren't cleared), but not longlife
17711784 else if (p -> as .free .flags != 0 && !longlived ) {
1772- push_freelist ( & eden_heaps_space , p ) ;
1785+ heaps_space = & eden_heaps_space ;
17731786 }
17741787 // If all else fails, use slower is_pointer_to_longlife_heap()
1775- else {
1776- push_freelist (is_pointer_to_longlife_heap (p ) ? & longlife_heaps_space : & eden_heaps_space , p );
1788+ else if (is_pointer_to_longlife_heap (p )) {
1789+ heaps_space = & longlife_heaps_space ;
1790+ } else {
1791+ heaps_space = & eden_heaps_space ;
17771792 }
1793+
1794+ push_freelist (heaps_space , p );
1795+ heaps_space -> total_free_slots ++ ;
17781796}
17791797
17801798static void
@@ -2129,13 +2147,14 @@ gc_sweep(heaps_space_t *heaps_space)
21292147#endif
21302148 if (gc_debug_stress ||
21312149 /* Shrink longlife if it's too lazy */
2132- (lt == lifetime_longlife && (total_free_slots > (heaps_space -> heap_slots_total - heap -> limit ) * longlife_laziness )) ||
2150+ (lt == lifetime_longlife && (total_free_slots > (heaps_space -> total_slots - heap -> limit ) * longlife_laziness )) ||
21332151 /* Shrink eden if there is a freeable heap and we are over our target size */
21342152 (lt == lifetime_eden && (heaps_space -> num_heaps > eden_heaps ))) {
21352153 GC_DEBUG_PRINTF (" %s heap freed (size %d)\n" , heaps_space_name , heap -> limit )
21362154 RVALUE * pp ;
2137- heaps_space -> heap_slots_total -= heap -> limit ;
2155+ heaps_space -> total_slots -= heap -> limit ;
21382156 heaps_space -> num_heaps -- ;
2157+ total_free_slots -= heap -> limit ;
21392158 heap -> limit = 0 ;
21402159 heap -> slotlimit = heap -> slot ;
21412160 for (pp = final_list ; pp != final ; pp = pp -> as .free .next ) {
@@ -2146,18 +2165,19 @@ gc_sweep(heaps_space_t *heaps_space)
21462165 }
21472166 }
21482167
2149- if ((lt == lifetime_longlife &&
2168+ for (i = 0 ;
2169+ i < heap_increase_rate &&
2170+ ((lt == lifetime_longlife &&
21502171 /* Expand longlife if it's not lazy enough */
2151- (total_free_slots < heaps_space -> heap_slots_total * longlife_laziness )) ||
2172+ (total_free_slots < heaps_space -> total_slots * longlife_laziness )) ||
21522173 (lt == lifetime_eden &&
2153- /* Add one eden heap at a time (to reduce initial fragmentation) until we reach
2174+ /* Add four eden heaps at a time (to reduce initial fragmentation) until we reach
21542175 the target size */
2155- (heaps_space -> num_heaps < eden_heaps ))) {
2176+ (heaps_space -> num_heaps < eden_heaps )));
2177+ i ++ ) {
21562178 new_heap_size = add_heap (heaps_space );
21572179 GC_DEBUG_PRINTF (" %s heap added (size %d)\n" , heaps_space_name , new_heap_size )
2158- #ifdef GC_DEBUG
21592180 total_free_slots += new_heap_size ;
2160- #endif
21612181 }
21622182
21632183 if (lt == lifetime_longlife ) {
@@ -2181,11 +2201,14 @@ gc_sweep(heaps_space_t *heaps_space)
21812201 free_unused_heaps ();
21822202 }
21832203
2204+ /* reset free slot count; we do it in bulk to avoid a cache penalty on every decrement */
2205+ heaps_space -> total_free_slots = total_free_slots ;
2206+
21842207#ifdef GC_DEBUG
21852208 if (GC_DEBUG_ON ) {
21862209 fprintf (gc_data_file , " %s heaps in heapspace: %8d\n" , heaps_space_name , heaps_space -> num_heaps );
21872210 fprintf (gc_data_file , " %s empty heaps: %8d\n" , heaps_space_name , empty_heaps );
2188- fprintf (gc_data_file , " %s total slots: %8lu \n" , heaps_space_name , total_live_slots + total_free_slots );
2211+ fprintf (gc_data_file , " %s total slots: %8u \n" , heaps_space_name , heaps_space -> total_slots );
21892212 fprintf (gc_data_file , " %s already free slots: %8lu\n" , heaps_space_name , total_already_freed_slots );
21902213 fprintf (gc_data_file , " %s finalized free slots: %8lu\n" , heaps_space_name , total_finalized_slots );
21912214 fprintf (gc_data_file , " %s live objects: %8lu\n" , heaps_space_name , total_live_slots );
@@ -2792,7 +2815,7 @@ garbage_collect_0(VALUE *top_frame)
27922815
27932816 /*** Schedule optional longlife GC based on allocation rate ***/
27942817
2795- if (longlife_recent_allocations > longlife_heaps_space .heap_slots_total * longlife_laziness ) {
2818+ if (longlife_recent_allocations > longlife_heaps_space .total_slots * longlife_laziness ) {
27962819 longlife_collection = Qtrue ;
27972820 }
27982821
@@ -2974,6 +2997,24 @@ rb_gc()
29742997 rb_gc_finalize_deferred ();
29752998}
29762999
3000+ VALUE
3001+ rb_gc_preemptive_start ()
3002+ {
3003+ if (GC_DEBUG_ON ) {
3004+ fprintf (gc_data_file , "*** Preemptive check ***\n" );
3005+ fprintf (gc_data_file , " Eden slots: %8u\n" , eden_heaps_space .total_slots );
3006+ fprintf (gc_data_file , " Free eden slots: %8u\n" , eden_heaps_space .total_free_slots );
3007+ fprintf (gc_data_file , " Required free slots: %8u\n" , eden_preemptive_total_free_slots );
3008+ }
3009+ if (eden_heaps_space .total_free_slots < eden_preemptive_total_free_slots ) {
3010+ garbage_collect ("preemptive collection request" );
3011+ return Qtrue ;
3012+ } else {
3013+ GC_DEBUG_PRINT (" Collection skipped\n" );
3014+ return Qfalse ;
3015+ }
3016+ }
3017+
29773018/*
29783019 * call-seq:
29793020 * GC.start => nil
@@ -3132,7 +3173,8 @@ void ruby_init_stack(VALUE *addr
31323173
31333174static void init_heaps_space (heaps_space_t * heaps_space , enum lifetime lifetime )
31343175{
3135- heaps_space -> heap_slots_total = 0 ;
3176+ heaps_space -> total_slots = 0 ;
3177+ heaps_space -> total_free_slots = 0 ;
31363178 heaps_space -> lifetime = lifetime ;
31373179}
31383180/*
@@ -3664,6 +3706,7 @@ Init_GC()
36643706
36653707 rb_mGC = rb_define_module ("GC" );
36663708 rb_define_singleton_method (rb_mGC , "start" , rb_gc_start , 0 );
3709+ rb_define_singleton_method (rb_mGC , "preemptive_start" , rb_gc_preemptive_start , 0 );
36673710 rb_define_singleton_method (rb_mGC , "enable" , rb_gc_enable , 0 );
36683711 rb_define_singleton_method (rb_mGC , "disable" , rb_gc_disable , 0 );
36693712 rb_define_method (rb_mGC , "garbage_collect" , rb_gc_start , 0 );
0 commit comments