Copy On Write


1回の書き込みでコピーする理由


値タイプの場合、変数または関数に渡されるパラメータに割り当てると、割り当てられた値がコピーされます.
ただし、非常に大きな価格タイプを処理するときにレプリケーションを行うと、コストが非常に高くなります.
したがって、これらの問題を最小限に抑えるために、特定の値タイプを変更したり、1回以上参照した場合にのみコピーしたりすることができ、パフォーマンスが向上する.
つまり、修正(書き込み)時にコピーします.
単純に値を渡すのではなく、既存の値を参照することで、不要なレプリケーションを削減し、メモリを節約できます.
操作例を見てみましょう.
変数配列の配列をarray 2に割り当てる場合、アドレス値は同じです.
array 2を変更すると、array 2のアドレス値が変更されます.
var array = [1,2,3] 
var array2 = array
address(o1: &array) //0x100517000 
address(o1: &array2) //0x100517000 

array2.append(4) // array2가 변경 
address(o1: &array) //0x100517000 
address(o1: &array2) //0x102b04330
  • アドレスの関数adress
    func address(o1: UnsafeRawPointer) {
        let address = String(format: "%p", Int(bitPattern: o1))
        print(address)
    
    }
    func address<T: AnyObject>(o2: UnsafePointer<T>) {
        let address = String(format: "%p", Int(bitPattern: o2))
        print(o2.pointee)
        print(address)
    
        withUnsafePointer(to: T.self) { pointer in
            pointer.pointee
            pointer
        }
    }
  • を求めます

    末尾の問題


    すべての値タイプに対して、書き込み時のコピーはサポートされていますか?


    NO
    COWは、SWIFT標準ライブラリで定義されている値タイプ「集合タイプ」(Array、Dictionary、Set)でのみ実現できます.カスタム値タイプでは、サポートされていない場合、ユーザーは直接COWを実現できます.

    カスタム構造で1回の書き込み時コピーを実現する方法について説明します。

    final class Ref<T> {
      var val : T
      init(_ v : T) {val = v}
    }
    
    struct Box<T> {
        var ref : Ref<T>
        init(_ x : T) { ref = Ref(x) }
    
        var value: T {
            get {
                return ref.val
            }
            set {
                if (!isKnownUniquelyReferenced(&ref)) {
                    //참조카운트 1이 아니라면 (=여러곳에서 참조되고 있다면)
                    print(isKnownUniquelyReferenced(&ref))
                ref = Ref(newValue)
                    print("새로운 인스턴스 생성")
                return
              }
              ref.val = newValue
            }
        }
    }
    
    //주소 구하는 함수
    func address(o1: UnsafeRawPointer) {
        let address = String(format: "%p", Int(bitPattern: o1))
        print(address)
    
    }
    var box = Box<[Int]>([1,2,3])
    var copy = box
    address(o1: &box.value) //0x10072bda0
    address(o1: &copy.value) //0x10072bda0
    
    box.value = [3]
    address(o1: &box.value) //**0x10072c1c0**
    address(o1: &copy.value) //0x10072bda0

  • Jenericタイプpropertyを持つclassタイプを定義します.

  • 上記で定義したクラスタイプpropertyを持つ構造を定義します.

  • 次にstructでvalueという計算プログラムを定義し、getterとsetterを持つようにします.

  • getterが呼び出されると、値が直接渡されます.

  • setterを呼び出す場合は、組み込まれたグローバル関数isKnownUniquelyReferenced関数を使用します.
    オブジェクトの参照数が1の場合はtrue、1より大きい場合はfalseを返します.
  • オブジェクトの参照数が2つより大きい場合は、他の場所で使用されていることを示し、新しいコピーを作成して変更します.
  • オブジェクトの参照数が1の場合、直接変更されます.
  • COW制限


    1回目の修正操作が2回目、3回目より長い→オーバーヘッド発生

    疑問点

  • は、必ずしも構造体のpropertyをクラスとして実現するものではないが、アドレス値は同じである.いったい何なのか.
  • は構造体にクラスを加えて実現するよりも、クラスで実現するほうがいいですか?
  • Reference


    https://medium.com/@lucianoalmeida1/understanding-swift-copy-on-write-mechanisms-52ac31d68f2f
    https://github.com/apple/swift/blob/main/docs/OptimizationTips.rst#id28