web-dev-qa-db-ja.com

コピーオンライトセマンティクスのメリット

コピーオンライトにはどのようなメリットがあるのでしょうか。当然、私は個人的な意見を期待していませんが、それが技術的かつ実際的に具体的な方法で有益である実際の実際的なシナリオ。そして、具体的には、&文字の入力を節約する以上のことを意味します。

明確にするために、この質問はデータ型のコンテキストにあり、割り当てまたはコピーの構築は暗黙的な浅いコピーを作成しますが、変更すると暗黙的なディープコピーが作成され、元のオブジェクトの代わりに変更が適用されます。

私が尋ねている理由は、デフォルトの暗黙的な動作としてCOWを使用することのメリットを見つけられないようです。多くのデータ型にCOWが実装されているQtを使用します。実際にはすべて、動的に割り当てられたストレージがいくつかあります。しかし、それは実際にユーザーにどのようなメリットがありますか?

例:

QString s("some text");
QString s1 = s; // now both s and s1 internally use the same resource

qDebug() << s1; // const operation, nothing changes
s1[o] = z; // s1 "detaches" from s, allocates new storage and modifies first character
           // s is still "some text"

この例でCOWを使用することで何が得られるでしょうか。

Const操作を使用するだけの場合、s1は冗長であり、sも使用できます。

値を変更する場合、COWは最初の非const操作までリソースコピーを遅らせるだけですが、暗黙的な共有の参照カウントをインクリメントして共有ストレージから切り離すという(最小限ではありますが)コストがかかります。 COWに伴うすべてのオーバーヘッドは無意味であるように見えます。

パラメータの受け渡しのコンテキストはそれほど変わらない-値を変更するつもりがない場合は、const参照として渡し、変更したい場合は、変更したくない場合は暗黙のディープコピーを作成する元のオブジェクト、または変更する場合は参照渡し。繰り返しになりますが、COWは不要なオーバーヘッドのように見え、何も実行されず、変更が元のオブジェクトから切り離されるため、必要な場合でも元の値を変更できないという制限が追加されます。

したがって、COWについて知っているかどうかに応じて、コードの目的が不明確になり、不要なオーバーヘッドが発生するか、予期しない動作を完全に混乱させて頭を悩ませる可能性があります。

私にとっては、不要なディープコピーを避けたい場合でも、作成するつもりでも、より効率的で読みやすいソリューションがあるようです。では、COWの実際的なメリットはどこにあるのでしょうか。このような人気のある強力なフレームワークで使用されているため、いくつかの利点があるに違いないと思います。

さらに、私が読んだことから、COWはC++標準ライブラリでは明示的に禁止されています。私がそれに見ている詐欺がそれと何か関係があるかどうかはわかりませんが、いずれにせよ、これには理由があるはずです。

10
dtech

書き込み時コピーは、オブジェクトのコピーを作成し、変更しないことが非常に多い状況で使用されます。それらの状況では、それはそれ自体で元が取れます。

あなたが述べたように、あなたはconstオブジェクトを渡すことができ、多くの場合それで十分です。ただし、constは、呼び出し元がそれを変更できないことを保証するだけです(もちろん、それらは_const_cast_でない限り)。マルチスレッドの場合や、コールバック(元のオブジェクトを変更する可能性がある)がある場合は処理しません。 COWオブジェクトを値で渡すと、APIユーザーではなくAPI開発者にこれらの詳細を管理するという課題が生じます。

C + 11の新しいルールは、特に_std::string_のCOWを禁止します。バッキングバッファが切り離されている場合、文字列のイテレータは無効にする必要があります。イテレータが_char*_として実装されていた場合(_string*_とインデックスではなく)、このイテレータは無効になります。 C++コミュニティは、イテレータが無効にされる頻度を決定する必要があり、その決定は_operator[]_がこれらのケースの1つであってはならないということでした。 _operator[]_に対する_std::string_は、変更される可能性がある_char&_を返します。したがって、_operator[]_は文字列を切り離し、イテレータを無効にする必要があります。これは貧弱な取引であると見なされ、end()cend()などの関数とは異なり、_operator[]_のconstバージョンを要求する方法はありません。 。 ( 関連 )。

COWはまだ生きており、STLの外でも十分です。特に、非常に軽量なオブジェクトのように見えるものの背後にある重いオブジェクトがあることをAPIのユーザーが期待することが不合理である場合に、それが非常に役立つことがわかりました。 COWをバックグラウンドで使用して、そのような実装の詳細に関係する必要がないようにすることができます。

15
Cort Ammon

文字列などの場合、文字列の一般的なケースは多くの場合小さな文字列であり、COWのオーバーヘッドは小さな文字列を単にコピーするコストをはるかに上回る傾向があるため、文字列などの場合、一般的なユースケースを悲観的にするようです。小さなバッファの最適化は、文字列のコピーの代わりにそのような場合のヒープ割り当てを回避するために、私にとって非常に意味があります。

ただし、Androidなどのより高いオブジェクトがあり、それをコピーしてサイバネティックアームを置き換えるだけの場合、COWは、Androidコピーに独自の腕を与えるだけです。その時点で永続的なデータ構造として不変にすることは優れているかもしれませんが、個々のAndroidパーツに適用された「部分的なCOW」は、これらの場合には妥当なようです。

そのような場合、Androidの2つのコピーは、同じ胴体、脚、足、頭、首、肩、骨盤などを共有/インスタンス化します。それらの間で異なり、共有されない唯一のデータ腕を上書きする際に2番目のAndroidのために独自に作成された腕です。

5
user204677