A fast reference to a global variable global_transient_heap becomes a function call to rb_objspace_get_theap() and related pointer chasing from vm -> objspace -> theap
Some internal transient heap structs moved to the header file now leaks into all other reference sites where this source file (transient_heap.c) as previously just used for API
I'm not sure exactly of the boundary Koichi had in mind for the GC compile module and how tightly it should (or shouldn't) be coupled to the transient heap. struct rb_objspace* declarations elsewhere for example reveals nothing about the structure members for example, whereas with this PR a lot of transient heap internals are exposed via the header file now
Also possible to move transient_heap.c into gc.c - I feel theap is not an experimental feature anymore and has been stable for quite some time with plausible performance benefits. The downside of that is gc.c is quite dense already, but then all ruby heap management concerns belong to one compile unit.
In a similar vein the global method cache could perhaps belong to the VM instance as well, effectively better alignment with MVM and also easier to have a balanced VM setup and teardown sequence without anything left dangling on ruby shutdown.
I'm positive about this, except for the performance.
Do you have any numbers?
Using the rdoc gc bench tooling Koichi used in https://bugs.ruby-lang.org/issues/14858, it's difficult to get consistent results between this change and master.
Master:
lourens@CarbonX1:~/src/ruby/trunk$ make gcbench-rdoc
Script: ./benchmark/gc/rdoc.rb
{:count=>137,
:heap_allocated_pages=>10124,
:heap_sorted_length=>10124,
:heap_allocatable_pages=>0,
:heap_available_slots=>4126494,
:heap_live_slots=>4117009,
:heap_free_slots=>9485,
:heap_final_slots=>0,
:heap_marked_slots=>2396930,
:heap_eden_pages=>10124,
:heap_tomb_pages=>0,
:total_allocated_pages=>10124,
:total_freed_pages=>0,
:total_allocated_objects=>24630985,
:total_freed_objects=>20513976,
:malloc_increase_bytes=>23046392,
:malloc_increase_bytes_limit=>32225676,
:minor_gc_count=>113,
:object_id_collisions=>0,
:major_gc_count=>24,
:remembered_wb_unprotected_objects=>2482,
:remembered_wb_unprotected_objects_limit=>4964,
:old_objects=>2387069,
:old_objects_limit=>4774138,
:oldmalloc_increase_bytes=>48011656,
:oldmalloc_increase_bytes_limit=>41096563}
ruby 2.7.0dev (2019-09-22T07:39:47Z master 2272efa463) [x86_64-linux] ["USE_RGENGC", "RGENGC_DEBUG", "RGENGC_ESTIMATE_OLDMALLOC", "GC_ENABLE_LAZY_SWEEP"]
./benchmark/gc/rdoc.rb
user system total real
18.842867 0.687732 19.530599 ( 19.722540)
GC total time (sec): 0
VmHWM: 411940 kB
Summary of rdoc on 2.7.0dev 19.722539804002736 0 137
(real time in sec, GC time in sec, GC count)
Objspace linked theap:
lourens@CarbonX1:~/src/ruby/ruby$ make gcbench-rdoc
Script: ./benchmark/gc/rdoc.rb
{:count=>137,
:heap_allocated_pages=>10122,
:heap_sorted_length=>10122,
:heap_allocatable_pages=>0,
:heap_available_slots=>4125809,
:heap_live_slots=>4116170,
:heap_free_slots=>9639,
:heap_final_slots=>0,
:heap_marked_slots=>2396677,
:heap_eden_pages=>10122,
:heap_tomb_pages=>0,
:total_allocated_pages=>10122,
:total_freed_pages=>0,
:total_allocated_objects=>24630955,
:total_freed_objects=>20514785,
:malloc_increase_bytes=>23055352,
:malloc_increase_bytes_limit=>32225676,
:minor_gc_count=>113,
:object_id_collisions=>0,
:major_gc_count=>24,
:remembered_wb_unprotected_objects=>2482,
:remembered_wb_unprotected_objects_limit=>4964,
:old_objects=>2387070,
:old_objects_limit=>4774140,
:oldmalloc_increase_bytes=>47981528,
:oldmalloc_increase_bytes_limit=>41096563}
ruby 2.7.0dev (2019-09-22T11:43:20Z objspace-theap 11b7839f09) [x86_64-linux] ["USE_RGENGC", "RGENGC_DEBUG", "RGENGC_ESTIMATE_OLDMALLOC", "GC_ENABLE_LAZY_SWEEP"]
./benchmark/gc/rdoc.rb
user system total real
18.517571 0.764057 19.281628 ( 19.303723)
GC total time (sec): 0
VmHWM: 411780 kB
Summary of rdoc on 2.7.0dev 19.303723024990177 0 137
(real time in sec, GC time in sec, GC count)
Array specific (adapted from the original issue to include RUBY_DESCRIPTION):
p RUBY_DESCRIPTION
require 'benchmark'
N = 10_000_000
def n_times str, args = ''
eval <<-EOS
proc{|max, #{args}|
i = 0
while i < max
#{str}
i+=1
end
}
EOS
end
m = n_times 'ary = Array.new(size)', 'size'
Benchmark.bm(10){|x|
0.step(to: 16){|i|
size = i
x.report(size){
m.call(N, size)
}
}
}
Some internal transient heap structs moved to the header file now leaks into all other reference sites where this source file (transient_heap.c) as previously just used for API
not a big issue, but I don't want to expose them...