Feature #9634

Updated by Narihiro Nakamura about 1 year ago

I've written a patch to collect most symbols.

PATCH: https://github.com/authorNari/ruby/compare/4a91fb7a45f0e3c...symbol_gc.patch https://github.com/authorNari/ruby/compare/13834fb3c...symbol_gc.patch

## Summary

* Most symbols in Ruby level are GC-able(generated by #to_sym, #intern, etc..)
* Exclude a symbol which is translated ID in C-level from GC-able symbols
* Keep Ruby's C extension compatibility
* Pass `make test-all`

## Benchmark

A benchmark program is here.

```
obj = Object.new
100_000.times do |i|
obj.respond_to?("sym#{i}".to_sym)
end
GC.start
puts"symbol : #{Symbol.all_symbols.size}"
```

```
% time RBENV_VERSION=ruby-r45059 ruby -v /tmp/a.rb
ruby 2.2.0dev (2014-02-20 trunk 45059) [x86_64-linux]
symbol : 102416
0.24s user 0.01s system 91% cpu 0.272 total

% time RBENV_VERSION=symgc ruby -v /tmp/a.rb
ruby 2.2.0dev (2014-02-20 trunk 45059) [x86_64-linux]
symbol : 2833
0.21s user 0.01s system 90% cpu 0.247 total
```

The total number of symbols is declined.
The total time of symgc version is improved because Full GC pressure has been reduced.

The result of `make benchmark`.

https://gist.github.com/authorNari/9359704

There is no significant slowdown.

(I would welcome to try an additional benchmark and report)

## Implementation Detail

I classify Dynamic symbol and Static symbol.

* Static symbol
* Generated by rb_itnern()
* A sequential unique number as in the past.
* Not GC-able
* LSB = 1
* Reserved IDs(147 and below) are exceptional cases

* Dynamic symbol
* Generated by #to_sym, #intern in Ruby level
* RVALUE
* GC-able
* LSB = 0
* Pin down a dynamic symbol when it translate to ID (e.g. SYM2ID, rb_intern).
* Pinned dynamic symbols are never collected.
* I'd like to include ID in GC's roots only CRuby internal in order to reduce pinned dynamic symbols.

Please read the patch if you want to know more information.

## Acknowledgment

The idea of this symbol GC is invented by Sasada Koichi in Heroku,inc.
Thank you.

-- ja --
RubyレベルのシンボルをGC対象にするパッチを書きました。
https://github.com/authorNari/ruby/compare/4a91fb7a45f0e3c...symbol_gc https://github.com/authorNari/ruby/compare/13834fb3c...symbol_gc

## 概要

* RubyレベルのほとんどのシンボルがGC対象(to_sym,internで作られたもの)
* C側でIDに変換された場合はGC対象から除外(rb_intern、SYM2IDなど)
* C-APIの互換性維持
* make test-allが通る

## ベンチマーク

以下のプログラムを実行。

```
obj = Object.new
100_000.times do |i|
obj.respond_to?("sym#{i}".to_sym)
end
GC.start
puts"symbol : #{Symbol.all_symbols.size}"
```

```
% time RBENV_VERSION=symgc ruby -v /tmp/a.rb
ruby 2.2.0dev (2014-02-20 trunk 45059) [x86_64-linux]
symbol : 2833
0.21s user 0.01s system 90% cpu 0.247 total

% time RBENV_VERSION=ruby-r45059 ruby -v /tmp/a.rb
ruby 2.2.0dev (2014-02-20 trunk 45059) [x86_64-linux]
symbol : 102416
0.24s user 0.01s system 91% cpu 0.272 total
```

総シンボル数が減少していることがわかる。
シンボル数の現象でFull GCのプレッシャーが削減されたことにより、symgcの速度が向上した。

make benchmarkの結果。
https://gist.github.com/authorNari/9359704

大幅な速度低下は見られない。

(上記以外の追試を歓迎します)

## (ちょっとした)詳細

symbolをstatic symbolとdynamic symbolに分類。

* static symbol
* rb_itnernなどで生成されたもの
* 従来通り、連番の一意な数値
* GC非対象
* 下位1ビットにフラグとして1を立てる
* 147以下の予約済みIDは例外ケース

* dynamic symbol
* Rubyレベルの#to_sym,#internなどで生成されたもの
* RVALUEとして生成
* GC対象
* 下位1ビットは0
* CレベルでID変換(SYM2IDなど)された場合、pindownし、GCで解放されなくなる
* Ruby内部でIDはルートに含め、pindownする箇所をなくしたい

その他の詳細はパッチを読んでもらえると…。

## 謝辞

シンボルGCのアイデアはHeroku社のささだこういち様によるものです。
ありがとうございます。

-----

Back