web-dev-qa-db-ja.com

Swiftすべての構造体の書き込み時にコピーしますか?

Swiftは配列の書き込み時にコピーするように最適化されますが、すべての構造体に対してこれを行うでしょうか?例:

struct Point {
   var x:Float = 0
}

var p1 = Point()
var p2 = p1 //p1 and p2 share the same data under the hood
p2.x += 1 //p2 now has its own copy of the data
27
gloo

Array is implementedコピーオンライト動作–コンパイラの最適化に関係なく取得されます(もちろん、最適化により、コピーが必要なケースの数を減らすことができます起こります)。

基本的なレベルでは、Arrayは、要素を含むヒープに割り当てられたバッファーへの参照を保持する構造にすぎません。したがって、複数のArrayインスタンスがsameバッファーを参照できます。特定の配列インスタンスを変更する場合、実装はバッファが一意に参照されているかどうかを確認し、参照されている場合は直接変更します。それ以外の場合、配列は値のセマンティクスを保持するために、基礎となるバッファーのコピーを実行します。

ただし、Point構造では、言語レベルでコピーオンライトを実装していません。もちろん @ Alexanderが言う のように、これはコンパイラーがあらゆる種類の最適化を実行して構造全体をコピーするコストを最小限に抑えることを妨げるものではありません。これらの最適化は、copy-on-writeの正確な動作に従う必要はありません。ただし、プログラムが言語仕様に従って実行されている限り、コンパイラーは自由に実行できますwhatever.

特定の例では、_p1_と_p2_の両方がグローバルであるため、同じモジュール内の他の.Swiftファイルがそれらにアクセスできるため、コンパイラーはそれらを別個のインスタンスにする必要があります(ただし、これは最適化される可能性があります)モジュール全体の最適化はありません)。ただし、コンパイラはインスタンスをコピーする必要はありません。 コンパイル時に浮動小数点の加算を評価 し、グローバルの1つを_0.0_で初期化し、もう1つを_1.0_を使用します。

そして、それらが関数のローカル変数である場合、例えば:

_struct Point {
    var x: Float = 0
}

func foo() {
    var p1 = Point()
    var p2 = p1
    p2.x += 1
    print(p2.x)
}

foo()
_

コンパイラーは、最初に2つのPointインスタンスを作成する必要さえありません。_1.0_に初期化された単一の浮動小数点ローカル変数を作成し、それを出力するだけです。

関数の引数として値の型を渡すことに関して、十分な大きさの型および(構造体の場合)十分なプロパティを利用する関数の場合、コンパイラ それらを渡すことができます参照により むしろコピーより。呼び出し先は、変更可能なコピーを使用する必要がある場合など、必要な場合にのみそれらのコピーを作成できます。

構造体が値で渡される他のケースでは、コンパイラが 特殊な関数 を使用して、関数が必要とするプロパティだけをコピーすることもできます。

次のコードの場合:

_struct Point {
    var x: Float = 0
    var y: Float = 1
}

func foo(p: Point) {
    print(p.x)
}

var p1 = Point()
foo(p: p1)
_

foo(p:)がコンパイラーによってインライン化されていないと想定します(この例ではそれが実装されますが、実装が特定のサイズに達すると、コンパイラーはそれを価値があるとは見なしません)–コンパイラーは関数を次のように特殊化できます。

_func foo(px: Float) {
    print(px)
}

foo(px: 0)
_

Pointxプロパティの値を関数に渡すだけなので、yプロパティをコピーするコストを節約できます。

したがって、コンパイラーは、値型のコピーを削減するために、できることは何でもします。しかし、さまざまな状況で非常に多くのさまざまな最適化が行われているため、任意の値型の最適化された動作を単純にコピーオンライトにまで煮詰めることはできません。

29
Hamish