どこまでランタイムに任せるかを 5 段階から選べます。
| 効率 | 全て有効で動きます。回収、コンパクション、止まる時間の上限。これが既定の姿です。言語が保証するのは、この姿です。 |
|---|---|
| 速度 | 要らない段階を、外せます。外した分だけ速く動きます。外すのはプログラマーの選択で、言語が保証する範囲外です。外した姿での動きは、プログラマーの責任になります。 |
効率性を下げるのは、速くするための調整です。 言語の側は、全て有効な姿だけを保証します。 そのため、5 通りの保証を引き受けることにはなりません。
下から順に、1 つずつ足していきます。上の段階は、下の段階を全部含みます。
| 足すもの | 何が変わるか | |
|---|---|---|
| 0 | 何も足しません手で確保し、手で返します。 | いまの C++ と同じです。 |
| 1 | 参照カウントと、即時の解放スマートポインタに相当します。 | 参照が無くなった瞬間に返します。 |
| 2 | 循環参照の回収 | 互いに参照し合ったものも返します。 |
| 3 | コンパクション | 空いた隙間を詰めます。ここで Java や C# と並びます。 |
| 4 | 止まる時間を、プログラマーが決める | 1 周期(= ゲームなら 1 フレーム)の持ち時間の内側に収めます。これが既定の姿です。 |
3 で Java や C# と並び、4 が、それらに無いものです。4 は速さの段ではなく、止まり方の選択です。3 に下げても速くはならず、上限の無いコンパクションになります。
※ 2 と 3 の間に、境が 1 つあります。コンパクションではオブジェクトが動きますので、参照の仕組みが変わります。そのため、同じビルドの中で、1・2 と、3 以上を混ぜることはできません(= 0 は任せない側で、どの段階とも同居できます)。部品(= クラスの単位)ごとに段階を選べますが、ビルド全体でどちら側かを先に決め、その側の中で部品ごとに選びます。
※ メモリをどう確保するか(= 起動時に最大値を一括で確保し、足りなくなった時に伸ばすか止まるかを選ぶ)は、段階ではなく設定です。どの段階でも選べます。足りなくなったときの振る舞いは、YamatoMM の 取り扱い方法 に書きました。
| 参照ごとに有効 | 3 の参照の仕組み。外せば、そのまま速くなります。ビルド全体で 1・2 の側を選んだときです。 |
|---|---|
| 代入ごとに有効 | 1 の参照カウントの増減。1 以上では外せません。 |
| 周期ごとに有効 | 3 のコンパクション。ただし、外すと速くなるとは限りません。隙間が埋まらないまま残り、一緒に使うオブジェクトが離れて置かれるので、キャッシュに当たりにくくなります。長く動かすと、外した方が遅くなることがあります。 |
どの段階が何をしているかを知れば、速さは見積もれます。 そのため、段階ごとの数は出していません。
コンパイラが証明できないことを人が保証する方法は、多くの言語にあります。
Rust の unsafe、C# の unsafe、Swift の UnsafePointer、Go の unsafe。
ただし、どれも「保証があるか、無いか」の 2 値です。Rust の unsafe の中でも借用や境界の検査は効きますが、許された操作(= 生ポインタの参照外しなど 5 つ)について、言語が保証するものはありません。
効率性は 5 段階です。下げても、何が残るかが判ります。 2 まで下げても「循環参照は回収する」という振る舞いは残り、3 と 4(= コンパクションと、止まる時間の上限)が外れます。
| 足すもの | Rust(= 言語と標準ライブラリ)にあるか | |
|---|---|---|
| 0 | 何も足しません | ありますBox(= 所有者が 1 人の確保)です。生の確保と解放は unsafe の中です。 |
| 1 | 参照カウントと、即時の解放 | ありますRc と Drop です。 |
| 2 | 循環参照の回収 | ありませんRc の循環参照は漏れますので、Weak を手で書きます。 |
| 3 | コンパクション | ありません回収の仕組みそのものがありません。 |
| 4 | 止まる時間を、プログラマーが決める | ありません回収で止まりませんので、概念が要りません。 |
目的も違います。
unsafe は、コンパイラが証明できないことを、人が保証するための方法です。
*C++ の各段階は、速くするための方法です。
unsafe が速さのために使われること(= 境界の検査を外すなど)もありますが、それは本来の目的ではなく、使い方です。
通常は、言語を選んだ時点で、メモリの管理の仕方も決まります。 Java や C# を選べば回収の仕組みを使い続け、Rust を選べば所有権の検査を受け続けます。 変えるには、別の言語で書き直すしかありません。 *C++ は、同じソースのまま、ビルドの仕方を変えます。 メモリの管理の仕方は、言語を選ぶときではなく、ビルドするときに決めます。
※ 速さは、お使いになる環境によって変わります。