[#114348] [Ruby master Feature#19832] Method#destructive?, UnboundMethod#destructive? — "sawa (Tsuyoshi Sawada) via ruby-core" <ruby-core@...>

Issue #19832 has been reported by sawa (Tsuyoshi Sawada).

15 messages 2023/08/06

[#114365] [Ruby master Bug#19834] Segmentation fault while running in docker — "ramachandran@... (Ramachandran A) via ruby-core" <ruby-core@...>

Issue #19834 has been reported by [email protected] (Ramachandran A).

7 messages 2023/08/09

[#114380] [Ruby master Bug#19837] Concurrent calls to Process.waitpid2 misbehave on Ruby 3.1 & 3.2 — "kjtsanaktsidis (KJ Tsanaktsidis) via ruby-core" <ruby-core@...>

Issue #19837 has been reported by kjtsanaktsidis (KJ Tsanaktsidis).

7 messages 2023/08/11

[#114399] [Ruby master Feature#19839] Need a method to check if two ranges overlap — "shouichi (Shouichi KAMIYA) via ruby-core" <ruby-core@...>

Issue #19839 has been reported by shouichi (Shouichi KAMIYA).

27 messages 2023/08/18

[#114410] [Ruby master Bug#19841] Marshal.dump stack overflow with recursive Time — "segiddins (Samuel Giddins) via ruby-core" <ruby-core@...>

Issue #19841 has been reported by segiddins (Samuel Giddins).

9 messages 2023/08/18

[#114422] [Ruby master Feature#19842] Intorduce M:N threads — "ko1 (Koichi Sasada) via ruby-core" <ruby-core@...>

Issue #19842 has been reported by ko1 (Koichi Sasada).

30 messages 2023/08/21

[#114590] [Ruby master Bug#19857] Eval coverage is reset after each `eval`. — "ioquatix (Samuel Williams) via ruby-core" <ruby-core@...>

Issue #19857 has been reported by ioquatix (Samuel Williams).

21 messages 2023/08/30

[ruby-core:114455] [Ruby master Feature#19783] Weak References in the GC

From: "ko1 (Koichi Sasada) via ruby-core" <ruby-core@...>
Date: 2023-08-23 01:06:35 UTC
List: ruby-core #114455
Issue #19783 has been updated by ko1 (Koichi Sasada).





* [Bug #19436] is fixed by checking inline method cache data structures.

* So the rest motivation is to support weak reference natively and I don't =
against about it.

* I have two concerns.



1. memory allocation during GC



Allocating memory during GC is not good idea in general (because it can be =
called when memory is not enough). How about to pass the data structure lik=
e that?



```C

struct weak_ref {

  VALUE v;

  struct weak_ref *prev;

};



rb_gc_mark_weak(struct weak_ref *ref)

{

  ref->prev =3D objspace->weaks;

  objspace->weaks =3D ref;



}



mark(){

  // do mark all



  // check weaks

  struct weak_ref *wref =3D objspace->weaks;

  while (wref) { ... }

}

```



It doesn't need more allocation while GC.

Making such imemo data is also acceptable.



2. huge wrefs



I understand it takes proportional time to marking wref counts (the number =
of `rb_gc_mark_weak()`). I think there is no so much wrefs (especially CME =
doesn't need it) but it can take a time if there are so many wrefs in an ap=
plication.



Could you make such benchmark?





----------------------------------------

Feature #19783: Weak References in the GC

https://bugs.ruby-lang.org/issues/19783#change-104217



* Author: peterzhu2118 (Peter Zhu)

* Status: Open

* Priority: Normal

----------------------------------------

GitHub PR: https://github.com/ruby/ruby/pull/8113



I'm proposing support for weak references in the Ruby garbage collector. Th=
is

feature adds a new function called `void rb_gc_mark_weak(VALUE *ptr)` which

marks `*ptr` as weak, meaning that if no other object strongly marks `*ptr`

(using `rb_gc_mark` or `rb_gc_mark_movable`), then it will be overwritten w=
ith

`*ptr =3D Qundef`.



Weak references are implemented using a buffer in `objspace` that stores all

the `ptr` in the latest marking phase. After marking has finished, we itera=
te

over the buffer and check if the `*ptr` is a dead object. If it is, then we

set `*ptr =3D Qundef`.



Weak references are implemented on the callable method entry (CME) of

callcaches, which fixes issue #19436.



Weak references are also implemented on `ObjectSpace::WeakMap` and

`ObjectSpace::WeakKeyMap`, which have:

- Significantly simpler implementations because we no longer need to have

  multiple tables and do not need to define finalizers on the objects.

- Support for compaction because finalizers pin objects and we no longer ne=
ed

  to define finalizers on the objects.

- Much faster performance (see [benchmarks](#microbenchmarks)).



## Metrics



This patch also adds two metrics, `GC.latest_gc_info(:weak_references_count=
)`

and `GC.latest_gc_info(:retained_weak_references_count)`. These two metrics

returns information about the number of weak references registered and the

number of weak references retained (references that did not point to a dead

object) in the last GC cycle.



## Benchmark results



### YJIT-bench



We see largely no change in performance or memory usage after this feature.



```

--------------  ---------  ----------  ---------  -----------  ----------  =
---------  --------------  -----------

bench           base (ms)  stddev (%)  RSS (MiB)  branch (ms)  stddev (%)  =
RSS (MiB)  branch 1st itr  base/branch

activerecord    72.3       2.2         51.9       72.9         2.2         =
51.9       0.99            0.99

chunky-png      889.2      0.3         43.9       874.5        0.3         =
42.5       1.02            1.02

erubi-rails     21.2       13.5        90.7       21.0         13.3        =
90.9       1.01            1.01

hexapdf         2557.0     0.8         157.1      2559.2       0.7         =
197.1      1.01            1.00

liquid-c        65.2       0.4         34.5       65.4         0.4         =
34.5       0.99            1.00

liquid-compile  62.5       0.4         30.9       62.2         0.4         =
31.0       1.00            1.01

liquid-render   164.6      0.4         33.1       162.6        0.3         =
33.1       1.01            1.01

mail            133.3      0.1         46.4       134.4        0.2         =
46.4       1.03            0.99

psych-load      2066.6     0.2         31.6       2083.6       0.1         =
31.6       0.99            0.99

railsbench      2027.0     0.5         88.8       2019.4       0.5         =
89.0       1.01            1.00

ruby-lsp        65.6       3.0         90.1       65.4         3.1         =
88.5       1.00            1.00

sequel          73.1       1.1         36.6       73.1         1.1         =
36.6       1.00            1.00

--------------  ---------  ----------  ---------  -----------  ----------  =
---------  --------------  -----------

```



### Microbenchmarks



We can see signficantly improved performance in `ObjectSpace::WeakMap`, with

`ObjectSpace::WeakMap#[]=3D` being nearly 3x faster.



Base:



```

ObjectSpace::WeakMap#[]=3D

                          1.037M (=B1 0.5%) i/s -      5.262M in   5.072833s

ObjectSpace::WeakMap#[]

                         12.367M (=B1 0.9%) i/s -     62.479M in   5.052365s

```



Branch:



```

ObjectSpace::WeakMap#[]=3D

                          3.054M (=B1 0.3%) i/s -     15.448M in   5.058783s

ObjectSpace::WeakMap#[]

                         15.796M (=B1 4.8%) i/s -     79.245M in   5.028583s

```



Code:



```ruby

require "bundler/inline"



gemfile do

  source "https://rubygems.org"

  gem "benchmark-ips"

end



wmap =3D ObjectSpace::WeakMap.new

key =3D Object.new

val =3D Object.new

wmap[key] =3D val



Benchmark.ips do |x|

  x.report("ObjectSpace::WeakMap#[]=3D") do |times|

    i =3D 0

    while i < times

      wmap[Object.new] =3D Object.new

      i +=3D 1

    end

  end



  x.report("ObjectSpace::WeakMap#[]") do |times|

    i =3D 0

    while i < times

      wmap[key]

      wmap[val] # does not exist

      i +=3D 1

    end

  end

end

```









--=20

https://bugs.ruby-lang.org/

 ______________________________________________
 ruby-core mailing list -- [email protected]
 To unsubscribe send an email to [email protected]
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-c=
ore.ml.ruby-lang.org/

In This Thread