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


10

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

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

私が尋ねている理由は、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 ++標準ライブラリでは明示的に禁止されています。私がそれに見ている詐欺がそれと何か関係があるかどうかはわかりませんが、いずれにせよ、これには理由があるはずです。

回答:


15

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

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

C + 11の新しい規則ではstd::string、特にCOWを禁止しています。文字列のイテレータは、バッキングバッファが切り離されている場合は無効にする必要があります。イテレータがchar*string*とインデックスではなく)として実装されていた場合、このイテレータは無効になります。C ++コミュニティは、反復子が無効にされる頻度を決定するoperator[]必要があり、その決定はそれらのケースの1つであってはならないということでした。 operator[]でがstd::string返さchar&れます。これは変更される場合があります。したがって、operator[]文字列を切り離してイテレータを無効にする必要があります。これは、貧しい貿易とみなす、などの機能とは違ったend()cend()、ののconstバージョンを依頼する方法はありませんoperator[]文字列をキャストのconstの短いです。(関連)。

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


イテレータを使用するか[]演算子を使用するかに関係なく、複数のスレッドで同じ文字列を変更することは、非常に悪いデザインのようです。したがって、COWは悪い設計を可能にします-それは多くの利点のようには聞こえません:)最後の段落のポイントは有効であるように見えますが、私自身は暗黙の振る舞いの大ファンではありません-人々は当然のことと考えがちですなぜコードが期待どおりに機能しないのかを理解するのに苦労し、暗黙の動作の背後に隠されているものを確認するために彼らが理解するまで疑問を持ち続けます。
dtech 2015年

使用のポイントに関しては、const_castconst参照によるパスを壊すのと同じくらい簡単にCOWを壊すことができるようです。たとえば、それをQString::constData()返しますconst QChar *- const_castそれとCOWは折りたたまれます-元のオブジェクトのデータを変更します。
dtech 2015年

COWからデータを返すことができる場合は、切り離す前に切り離すか、COWに対応した形式でデータを返す必要があります(char*明らかに対応していません)。暗黙の振る舞いに関しては、私はあなたが正しいと思います、それには問題があります。APIの設計は、両極端の間の一定のバランスです。暗黙的すぎて、人々はそれが事実上仕様の一部であるかのように、特別な動作に依存し始めます。明示的すぎると、あまり重要ではない根本的な詳細が多すぎてAPI仕様に突然書き込まれるため、APIが扱いにくくなります。
Cort Ammon

stringコンパイラの設計者が、大量のコードがconst-referenceを使用するのではなく文字列をコピーしていることに気付いたため、クラスがCOWの動作をしたと思います。彼らがCOWを追加した場合、彼らはこのケースを最適化し、より多くの人々を幸せにすることができます(C ++ 11までは合法でした)。私は彼らの立場に感謝します:私は常に文字列をconst参照で渡しますが、読みやすさを損なう構文のジャンクをすべて見ました。const std::shared_ptr<const std::string>&正しいセマンティクスを取得するためだけに書くのは嫌いです!
Cort Ammon

5

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

ただし、Androidのようなより重いオブジェクトがあり、それをコピーしてそのサイバネティックアームを置き換えるだけの場合、COWは変更可能な構文を維持しながら、Android全体をディープコピーする必要をなくすための方法として非常に合理的です。コピーにユニークな腕を与えます。その時点で永続的なデータ構造として不変にすることは優れているかもしれませんが、個々のAndroidパーツに適用される「部分的なCOW」は、これらの場合には妥当なようです。

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


これはすべて問題ありませんが、COWを必要とせず、多くの有害な暗黙の影響を受けます。また、欠点もあります。オブジェクトのインスタンス化が必要になることがよくあります。型のインスタンス化を意味するのではなく、オブジェクトをインスタンスとしてコピーするため、ソースオブジェクトを変更すると、コピーも更新されます。COWは単に「可能性」を排除します。「共有」オブジェクトへの変更はそれを切り離すからです。
dtech 2015

正確性IMOは、暗黙の動作ではなく、達成が「簡単」であってはなりません。正確性の良い例はCONSTの正確性です。これは明白であり、あいまいさや目に見えない副作用の余地がないためです。この「簡単」で自動のようなものは、物事がどのように機能するかについての追加のレベルの理解を構築することは決してありません。これは全体的な生産性にとって重要であるだけでなく、望ましくない動作の可能性をかなり排除します。 。COWを使用して暗黙的に可能になったことはすべて、明示的に実現することも簡単で、より明確です。
dtech

私が取り組んでいる言語でデフォルトでCOWを提供するかどうかというジレンマが私の質問の動機となりました。プロとコンの重み付けを行った後、デフォルトではなく、新しいタイプまたは既存のタイプの両方に適用できるモディファイアとして使用することにしました。両方の長所のように思えますが、明示的にCOWを使用したい場合は、COWの暗黙性を保持できます。
dtech 2015

@ddriver私たちが持っているものはノードのパラダイムを備えたプログラミング言語に似ていますが、ノードの使用値のセマンティクスの種類と参照型のセマンティクスはありません(おそらく、C ++ 11でセマンティクスを取得して移動するstd::vector<std::string>前にいくらか似ていemplace_backます) 。ただし、基本的にはインスタンス化も使用しています。ノードシステムはデータを変更する場合と変更しない場合があります。入力には何もせず、コピーを出力するだけのパススルーノードのようなものがあります(彼のプログラムのユーザー編成のためにあります)。それらの場合、すべてのデータは複合型の浅いコピーされます...

@ddriverコピーオンライトは、事実上、「変更時に暗黙的にインスタンスを一意にする」種類のコピープロセスです。オリジナルを変更することは不可能になります。オブジェクトAがコピーされ、オブジェクトに対して何も行われない場合はB、メッシュなどの複雑なデータ型の安価な浅いコピーです。ここで、を変更するBと、変更するデータはBCOWを通じて一意になりますが、変更Aされません(一部のアトミック参照カウントを除く)。
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.