Nim作者のAraqは長年、Nimオリジナルの、これまでよりもより効率の良いGCをNimに組み込む研究をしてきました。そして今年リリースされたv1.2においてARCが、そしてv1.4ではORCが使えるようになりました。
この新しいGCであるARCとORCとは一体何なのか、これまでの参照カウントやマークアンドスウィープとはどう違っているのかについて解説していきます。
これは何?
ARC及びORCは全く新しい発想で作られたGCではありません。しかしそれは「ムーブセマンティクス」を最適化させた参照カウントです。
Nimはコンパイル言語です。NimのソースコードをCにコンパイルし、GCCによってCをバイナリにコンパイルしますが、その時にNimのソースコードにデストラクタを自動で挿入します。
proc main =
let mystr = stdin.readLine()
case mystr
of "hello":
echo "Nice to meet you!"
of "bye":
echo "Goodbye!"
quit()
else:
discard
main()
↓ 自動で変換
var mystr
try:
mystr = readLine(stdin)
case mystr
of "hello":
echo ["Nice to meet you too!"]
of "bye":
echo ["Goodbye!"]
quit(0)
else:
discard
finally:
`=destroy`(mystr)
スコープが終了した後にデストラクタを必要とする変数のデストラクタ呼び出しを、Nimコンパイラが自動的に挿入します。これによって「スコープベースのメモリ管理」ができるようになります。
これらの仕組みのおかげで、ARCはこれまでの参照カウントに比べて以下のような利点があります。
- ムーブセマンティクス
- コンパイラがプログラムを静的に分析し、メモリコピーを可能な限り参照に変換する能力。
- 共有ヒープ
- 異なるスレッドが同じメモリにアクセスし、スレッド間で変数を渡すために変数をコピーする必要がない。スレッド間でのデータの分離と送信に関するRFCも参照してください)
- より簡単な FFI
- 参照カウントは各外部スレッドが手動で GC をセットアップする必要がありますが、ARC ではそうではない。これはまた、他の言語(.dll、.so、Python拡張など)から使用するためのNimライブラリを作成するためには、ARCの方がはるかに良い選択であることを意味する。
- ハードなリアルタイムプログラミングに適している。
- コピーの省略
- 多くの場合、コピーを単純な参照にすることができる。
このように、ARCを使えば多くの場合でプログラムを高速化し、メモリ使用量を減らし、予測可能な挙動を持つようにできます。
しかしARCはあくまで参照カウントであるため、例えば変数AがBに依存し、変数BがCに依存し、変数CがAに依存しているような循環参照の場合には参照カウントは上手く機能できません。これらのサイクルを見つけてGCを動かすには、サイクルカウンターが必要です。
これまでのNimの参照カウントGCではサイクルカウンターはマークアンドスウィープで実装されてきましたが、これをARCをベースにして新しく作ることになりました。
ORC
ORCはARCをベースにしたNimの全く新しいサイクルコレクタです。グローバルトレースしかないその他のGCとは違い、ORCはスコープの単位をベースにしたローカルトレースを含み、処理するべきサイクルトレースを含むので、得に非同期処理の時に使われるべきです。
ORCは今後のアップデートでNim標準のGCになる予定です。
使い方
Nimのバージョン1.2以降で使えるようになっていますが、いくつかのバグ修正を含んだ1.4以降で使うことが推奨されています。
コンパイルオプションに指定するだけで簡単に有効になります。
nim c --gc:arc main
nim c --gc:orc main
ベンチマーク
参考文献
Introduction to ARC/ORC in Nim
こちらはNim作者Araq本人によるプレゼンです。英語のわかる方、興味を持った方がぜひご覧になってみてください。
https://www.youtube.com/watch?v=yA32Wxl59wo