| Age | Commit message (Collapse) | Author |
|
https://github.com/ruby/mmtk/commit/e5e2c1c347
|
|
https://github.com/ruby/mmtk/commit/87290e45b2
|
|
https://github.com/ruby/mmtk/commit/59d27203e2
|
|
https://github.com/ruby/mmtk/commit/e34d5cf32f
|
|
https://github.com/ruby/mmtk/commit/350625ebb3
|
|
Redos commit 544770d which seems to have accidentally been undone in b27d935.
|
|
|
|
https://github.com/ruby/mmtk/commit/308936296a
|
|
This allows the mutator thread to dump its backtrace when a GC thread crashes.
https://github.com/ruby/mmtk/commit/40ff9ffee7
|
|
https://github.com/ruby/mmtk/commit/c3e338bb25
|
|
The argument to `is_data_encoding` is assumed to be `T_DATA`.
|
|
This commit allows objects that are safe to be freed in parallel to do so.
A decrease in object freeing time can be seen in profiles.
The benchmarks don't show much difference.
Before:
-------------- -------------------- ---------- ---------
bench sequential free (ms) stddev (%) RSS (MiB)
activerecord 242.3 7.4 84.3
chunky-png 439.1 0.6 75.6
erubi-rails 1221.2 4.2 132.7
hexapdf 1544.8 1.8 429.1
liquid-c 42.7 7.4 48.5
liquid-compile 41.4 8.3 52.2
liquid-render 100.6 3.0 56.8
mail 108.9 2.1 65.1
psych-load 1536.9 0.6 43.4
railsbench 1633.5 2.6 146.2
rubocop 126.5 15.8 142.1
ruby-lsp 129.6 9.7 112.2
sequel 47.9 6.5 44.6
shipit 1152.0 2.7 315.2
-------------- -------------------- ---------- ---------
After:
-------------- ------------------ ---------- ---------
bench parallel free (ms) stddev (%) RSS (MiB)
activerecord 235.1 5.5 87.4
chunky-png 440.8 0.8 68.1
erubi-rails 1105.3 0.8 128.0
hexapdf 1578.3 4.1 405.1
liquid-c 42.6 7.1 48.4
liquid-compile 41.5 8.1 52.1
liquid-render 101.2 2.8 53.3
mail 109.7 2.7 64.8
psych-load 1567.7 1.1 44.4
railsbench 1644.9 1.9 150.9
rubocop 125.6 15.4 148.5
ruby-lsp 127.9 5.8 104.6
sequel 48.2 6.1 44.1
shipit 1215.3 4.7 320.5
-------------- ------------------ ---------- ---------
https://github.com/ruby/mmtk/commit/4f0b5fd2eb
|
|
https://github.com/ruby/mmtk/commit/290a2aec4e
|
|
|
|
This commit implements moving Immix in MMTk, which allows objects to move
in the GC.
The performance of this implementation is not yet amazing. It is very
similar to non-moving Immix in many of them and slightly slower in others.
The benchmark results is shown below.
-------------- ----------------- ---------- ---------
bench Moving Immix (ms) stddev (%) RSS (MiB)
activerecord 241.9 0.5 86.6
chunky-png 447.8 0.8 74.9
erubi-rails 1183.9 0.8 136.1
hexapdf 1607.9 2.6 402.3
liquid-c 45.4 6.7 44.9
liquid-compile 44.1 9.3 53.0
liquid-render 105.4 4.5 55.9
lobsters 650.1 9.7 418.4
mail 115.4 2.1 64.4
psych-load 1656.8 0.8 43.6
railsbench 1653.5 1.3 149.8
rubocop 127.0 15.6 142.1
ruby-lsp 130.7 10.5 99.4
sequel 52.8 7.2 45.6
shipit 1187.0 3.9 311.0
-------------- ----------------- ---------- ---------
-------------- --------------------- ---------- ---------
bench Non-moving Immix (ms) stddev (%) RSS (MiB)
activerecord 218.9 2.7 86.1
chunky-png 464.6 0.8 66.7
erubi-rails 1119.0 4.3 132.7
hexapdf 1539.8 1.8 425.2
liquid-c 40.6 6.9 45.2
liquid-compile 40.6 8.1 52.9
liquid-render 99.3 2.3 48.3
mail 107.4 5.3 65.4
psych-load 1535.6 1.0 39.5
railsbench 1565.6 1.1 149.6
rubocop 122.5 14.3 146.7
ruby-lsp 128.4 10.7 106.4
sequel 44.1 4.0 45.7
shipit 1154.5 2.7 358.5
-------------- --------------------- ---------- ---------
|
|
|
|
https://github.com/ruby/mmtk/commit/c4cca6c1c3
|
|
Adding a fast path for bump pointer allocator can improve allocation
performance.
For the following microbenchmark with MMTK_HEAP_MIN=100MiB:
10_000_000.times { String.new }
Before:
810.7 ms ± 8.3 ms [User: 790.9 ms, System: 40.3 ms]
After:
777.9 ms ± 10.4 ms [User: 759.0 ms, System: 37.9 ms]
https://github.com/ruby/mmtk/commit/0ff5c9f579
|
|
https://github.com/ruby/mmtk/commit/2185189df4
|
|
This will allow the Ruby backtrace, memory mapping, etc. to be outputted
when a Ruby mutator thread panics.
https://github.com/ruby/mmtk/commit/d10fd325dd
|
|
https://github.com/ruby/mmtk/commit/ed9036c295
|
|
https://github.com/ruby/mmtk/commit/4e789e118b
|
|
This reverts commit 228d13f6ed914d1e7f6bd2416e3f5be8283be865.
This commit makes default.c and mmtk.c depend on shape.h, which prevents
them from building independently.
|
|
Attempt to fix the following SEGV:
```
ruby(gc_mark) ../src/gc/default/default.c:4429
ruby(gc_mark_children+0x45) [0x560b380bf8b5] ../src/gc/default/default.c:4625
ruby(gc_mark_stacked_objects) ../src/gc/default/default.c:4647
ruby(gc_mark_stacked_objects_all) ../src/gc/default/default.c:4685
ruby(gc_marks_rest) ../src/gc/default/default.c:5707
ruby(gc_marks+0x4e7) [0x560b380c41c1] ../src/gc/default/default.c:5821
ruby(gc_start) ../src/gc/default/default.c:6502
ruby(heap_prepare+0xa4) [0x560b380c4efc] ../src/gc/default/default.c:2074
ruby(heap_next_free_page) ../src/gc/default/default.c:2289
ruby(newobj_cache_miss) ../src/gc/default/default.c:2396
ruby(RB_SPECIAL_CONST_P+0x0) [0x560b380c5df4] ../src/gc/default/default.c:2420
ruby(RB_BUILTIN_TYPE) ../src/include/ruby/internal/value_type.h:184
ruby(newobj_init) ../src/gc/default/default.c:2136
ruby(rb_gc_impl_new_obj) ../src/gc/default/default.c:2500
ruby(newobj_of) ../src/gc.c:996
ruby(rb_imemo_new+0x37) [0x560b380d8bed] ../src/imemo.c:46
ruby(imemo_fields_new) ../src/imemo.c:105
ruby(rb_imemo_fields_new) ../src/imemo.c:120
```
I have no reproduction, but my understanding based on the backtrace
and error is that GC is triggered inside `newobj_init` causing the
new object to be marked while in a incomplete state.
I believe the fix is to pass the `shape_id` down to `newobj_init`
so it can be set before the GC has a chance to trigger.
|
|
In rb_gc_impl_before_fork, it locks the VM and barriers all the Ractors
before calling mmtk_before_fork. However, since rb_mmtk_block_for_gc is
a barrier point, one or more Ractors could be paused there. However,
mmtk_before_fork is not compatible with that because it assumes that the
MMTk workers are idle, but the workers are not idle because they are
busy working on a GC.
This commit essentially implements a trylock. It will optimistically
lock but will release the lock if it detects that any other Ractors are
waiting in rb_mmtk_block_for_gc.
For example, the following script demonstrates the issue:
puts "Hello #{Process.pid}"
100.times do |i|
puts "i = #{i}"
Ractor.new(i) do |j|
puts "Ractor #{j} hello"
1000.times do |i|
s = "#{j}-#{i}"
end
Ractor.receive
puts "Ractor #{j} goodbye"
end
pid = fork { }
puts "Child pid is #{pid}"
_, status = Process.waitpid2 pid
puts status.success?
end
puts "Goodbye"
We can see the MMTk worker thread is waiting to start the GC:
#4 0x00007ffff66538b1 in rb_mmtk_stop_the_world () at gc/mmtk/mmtk.c:101
#5 0x00007ffff6d04caf in mmtk_ruby::collection::{impl#0}::stop_all_mutators<mmtk::scheduler::gc_work::{impl#14}::do_work::{closure_env#0}<mmtk::plan::immix::gc_work::ImmixGCWorkContext<mmtk_ruby::Ruby, 0>>> (_tls=..., mutator_visitor=...) at src/collection.rs:23
However, the mutator thread is stuck in mmtk_before_fork trying to stop
that worker thread:
#4 0x00007ffff6c0b621 in std::sys::thread::unix::Thread::join () at library/std/src/sys/thread/unix.rs:134
#5 0x00007ffff6658b6e in std::thread::JoinInner<()>::join<()> (self=...)
#6 0x00007ffff6658d4c in std::thread::JoinHandle<()>::join<()> (self=...)
#7 0x00007ffff665795e in mmtk_ruby::binding::RubyBinding::join_all_gc_threads (self=0x7ffff72462d0 <mmtk_ruby::BINDING+8>) at src/binding.rs:115
#8 0x00007ffff66561a8 in mmtk_ruby::api::mmtk_before_fork () at src/api.rs:309
#9 0x00007ffff66556ff in rb_gc_impl_before_fork (objspace_ptr=0x555555d17980) at gc/mmtk/mmtk.c:1054
#10 0x00005555556bbc3e in rb_gc_before_fork () at gc.c:5429
https://github.com/ruby/mmtk/commit/1a629504a7
|
|
We need the VM barrier in rb_gc_impl_before_fork to stop the other Ractors
because otherwise they could be allocating objects in the fast path which
could be calling mmtk_add_obj_free_candidate. Since mmtk_add_obj_free_candidate
acquires a lock on obj_free_candidates in weak_proc.rs, this lock may not
be released in the child process after the Ractor dies.
For example, the following script demonstrates the issue:
puts "Hello #{Process.pid}"
100.times do |i|
puts "i = #{i}"
Ractor.new(i) do |j|
puts "Ractor #{j} hello"
1000.times do |i|
s = "#{j}-#{i}"
end
Ractor.receive
puts "Ractor #{j} goodbye"
end
pid = fork { }
puts "Child pid is #{pid}"
_, status = Process.waitpid2 pid
puts status.success?
end
puts "Goodbye"
In the child process, we can see that it is stuck trying to acquire the
lock on obj_free_candidates:
#5 0x00007192bfb53f10 in mmtk_ruby::weak_proc::WeakProcessor::get_all_obj_free_candidates (self=0x7192c0657498 <mmtk_ruby::BINDING+72>) at src/weak_proc.rs:52
#6 0x00007192bfa634c3 in mmtk_ruby::api::mmtk_get_all_obj_free_candidates () at src/api.rs:295
#7 0x00007192bfa61d50 in rb_gc_impl_shutdown_call_finalizer (objspace_ptr=0x578c17abfc50) at gc/mmtk/mmtk.c:1032
#8 0x0000578c1601e48e in rb_ec_finalize (ec=0x578c17ac06d0) at eval.c:166
#9 rb_ec_cleanup (ec=<optimized out>, ex=<optimized out>) at eval.c:257
#10 0x0000578c1601ebf6 in ruby_cleanup (ex=<optimized out>) at eval.c:180
#11 ruby_stop (ex=<optimized out>) at eval.c:292
#12 0x0000578c16127124 in rb_f_fork (obj=<optimized out>) at process.c:4291
#13 rb_f_fork (obj=<optimized out>) at process.c:4281
https://github.com/ruby/mmtk/commit/eb4b229858
|
|
If we are using multiple Ractors, other Ractors may allocate objects after
rb_gc_impl_before_fork is ran because it does not lock the VM. This can cause
the GC to be in a bad state since rb_gc_impl_before_fork may have terminated
GC threads so a GC cannot run until rb_gc_impl_after_fork is ran.
https://github.com/ruby/mmtk/commit/e4bea5676d
|
|
rb_gc_impl_shutdown_call_finalizer
https://github.com/ruby/mmtk/commit/1828f6596f
|
|
rb_gc_verify_shareable is not GC implementation specific so it should live
in gc.c.
|
|
|
|
|
|
|
|
If a reference marked weak becomes a special const, it will crash because
it is not a GC handled object. We should skip special consts here.
https://github.com/ruby/mmtk/commit/870a79426b
|
|
This commit adds file and line to GC VM locking functions for debugging
purposes and adds upper case macros to pass __FILE__ and __LINE__.
Notes:
Merged: https://github.com/ruby/ruby/pull/13550
|
|
Some GC implementations want to always know when an object is written to,
even if the written value is a special constant. Checking special constants
in rb_obj_written was a micro-optimization that made assumptions about
the GC implementation.
Notes:
Merged: https://github.com/ruby/ruby/pull/13497
|
|
https://github.com/ruby/mmtk/commit/fdc13963f0
|
|
This makes `RBobject` `4B` larger on 32 bit systems
but simplifies the implementation a lot.
[Feature #21353]
Co-authored-by: Jean Boussier <byroot@ruby-lang.org>
Notes:
Merged: https://github.com/ruby/ruby/pull/13341
|
|
The 0th element of the finalizer table array should be the object ID.
https://github.com/ruby/mmtk/commit/75e4a82652
|
|
We should get the object ID for finalizers in rb_gc_impl_define_finalizer
instead of when we create the finalizer job in make_final_job because
when we are in multi-Ractor mode, object ID needs to walk the references
which allocates an identity hash table. We cannot allocate in make_final_job
because it is in a MMTk worker thread.
https://github.com/ruby/mmtk/commit/922f22a690
|
|
Fix a regression introduced by: https://github.com/ruby/ruby/pull/13155
Notes:
Merged: https://github.com/ruby/ruby/pull/13350
|
|
Notes:
Merged: https://github.com/ruby/ruby/pull/13350
|
|
The table is global so accesses must be synchronized.
Notes:
Merged: https://github.com/ruby/ruby/pull/13349
|
|
And get rid of the `obj_to_id_tbl`
It's no longer needed, the `object_id` is now stored inline
in the object alongside instance variables.
We still need the inverse table in case `_id2ref` is invoked, but
we lazily build it by walking the heap if that happens.
The `object_id` concern is also no longer a GC implementation
concern, but a generic implementation.
Co-Authored-By: Matt Valentine-House <matt@eightbitraptor.com>
Notes:
Merged: https://github.com/ruby/ruby/pull/13159
|
|
|
|
`objspace->finalizer_table` must be synchronized,
otherwise concurrent insertion from multiple ractors
will cause a crash.
Repro:
```ruby
ractors = 5.times.map do |i|
Ractor.new do
100_000.times.map do
o = Object.new
ObjectSpace.define_finalizer(o, ->(id) {})
o
end
end
end
ractors.each(&:take)
```
Notes:
Merged: https://github.com/ruby/ruby/pull/13151
|
|
This inverse table is only useful if `ObjectSpace._id2ref` is used,
which is extremely rare. The only notable exception is the `drb` gem
and even then it has an option not to rely on `_id2ref`.
So if we assume this table will never be looked up, we can just
not maintain it, and if it turns out `_id2ref` is called, we
can lock the VM and re-build it.
```
compare-ruby: ruby 3.5.0dev (2025-04-10T09:44:40Z master 684cfa42d7) +YJIT +PRISM [arm64-darwin24]
built-ruby: ruby 3.5.0dev (2025-04-10T10:13:43Z lazy-id-to-obj d3aa9626cc) +YJIT +PRISM [arm64-darwin24]
warming up..
| |compare-ruby|built-ruby|
|:----------|-----------:|---------:|
|baseline | 26.364M| 25.974M|
| | 1.01x| -|
|object_id | 10.293M| 14.202M|
| | -| 1.38x|
```
Notes:
Merged: https://github.com/ruby/ruby/pull/13115
|
|
We rely on scan_vm_specific_roots to reach all stacks via the following
path:
VM -> ractors -> threads -> fibers -> stacks
https://github.com/ruby/mmtk/commit/0a6a835aaa
|
|
ractor_cache will always be NULL in this context
Notes:
Merged: https://github.com/ruby/ruby/pull/13031
|
|
That seemed like the logical thing to do to me, but ko1 disagree.
Notes:
Merged: https://github.com/ruby/ruby/pull/13008
|
|
[Bug #20271]
[Bug #20267]
[Bug #20255]
`rb_obj_alloc(RBASIC_CLASS(obj))` will always allocate from the basic
40B pool, so if `obj` is larger than `40B`, we'll create a corrupted
object when we later copy the shape_id.
Instead we can use the same logic than ractor copy, which is
to use `rb_obj_clone`, and later ask the GC to free the original
object.
We then must turn it into a `T_OBJECT`, because otherwise
just changing its class to `RactorMoved` leaves a lot of
ways to keep using the object, e.g.:
```
a = [1, 2, 3]
Ractor.new{}.send(a, move: true)
[].concat(a) # Should raise, but wasn't.
```
If it turns out that `rb_obj_clone` isn't performant enough
for some uses, we can always have carefully crafted specialized
paths for the types that would benefit from it.
Notes:
Merged: https://github.com/ruby/ruby/pull/13008
|