프로그래밍언어

[Swift] COW (Copy-On-Write)

애기공룡훈련병 2025. 2. 16. 07:49
반응형

1. Copy-on-Write(COW)란?

Copy-on-Write(COW)는 값 타입(Value Type)의 복사 비용을 최적화하기 위한 메모리 관리 기법입니다.
일반적으로 Swift에서 값 타입(예: struct, enum)은 참조가 아닌 복사를 통해 전달됩니다. 하지만, 값이 변하지 않는 한 굳이 복사할 필요가 없으므로, 공유된 데이터에 대한 변경이 일어나기 전까지 복사를 미루는 전략이 COW입니다.

2. Copy-on-Write의 동작 원리

Copy-on-Write는 참조 카운팅을 활용하여 공유된 데이터인지 확인하고, 변경이 발생할 때만 실제 복사를 수행하는 방식으로 동작합니다.

COW의 동작 과정

  1. 초기 상태:
    • 새로운 변수에 값을 할당하면, 내부적으로 같은 메모리를 공유합니다.
    • 참조 카운트가 증가하며, 원본 데이터를 그대로 사용합니다.
  2. 읽기(Read) 시:
    • 값 타입이지만 변경되지 않는 경우, 같은 메모리를 계속 공유하여 성능을 최적화합니다.
  3. 쓰기(Write) 시:
    • 변경이 발생하면, Swift는 새로운 메모리 공간을 할당하고 데이터를 복사합니다.
    • 즉, 변경이 필요할 때만 복사가 수행되므로 불필요한 메모리 복사를 방지할 수 있습니다.

Swift의 Copy-on-Write 예제

var array1 = [1, 2, 3] // 새로운 배열 생성
var array2 = array1    // 같은 메모리를 공유

print(array1 === array2) // false (Swift에서는 값 타입이므로 === 사용 불가)

array2.append(4) // 변경 발생 -> 새로운 메모리 공간에 복사 후 수정
print(array1)    // [1, 2, 3]
print(array2)    // [1, 2, 3, 4]

위 코드에서 array1array2는 초기에 같은 메모리를 공유하지만, array2.append(4)를 수행하는 순간 복사가 발생하여 array2만 새로운 메모리 공간을 갖게 됩니다.

3. Copy-on-Write를 사용하는 Swift의 타입

Swift에서는 다음과 같은 컬렉션 타입이 Copy-on-Write 최적화를 지원합니다:

  • Array
  • Dictionary
  • Set
  • String (Swift 5부터 String은 COW를 지원하는 값 타입)

또한, 사용자 정의 타입에서도 Copy-on-Write를 구현할 수 있습니다.

4. Copy-on-Write를 고려한 성능 최적화

Copy-on-Write를 활용하여 성능을 최적화할 수 있는 몇 가지 방법을 소개합니다.

isKnownUniquelyReferenced 활용

isKnownUniquelyReferenced 함수를 사용하면 현재 객체가 단독으로 참조되고 있는지 확인할 수 있습니다.

예제

final class Wrapper<T> {
    var value: T
    init(_ value: T) { self.value = value }
}

struct COWStruct {
    private var storage: Wrapper<[Int]>
    
    init(_ elements: [Int]) {
        storage = Wrapper(elements)
    }
    
    mutating func mutate() {
        if !isKnownUniquelyReferenced(&storage) {
            storage = Wrapper(storage.value) // 복사 수행
        }
        storage.value.append(100)
    }
}

var cow1 = COWStruct([1, 2, 3])
var cow2 = cow1 // 같은 데이터를 공유

cow2.mutate() // 복사 발생

print(cow1) // [1, 2, 3]
print(cow2) // [1, 2, 3, 100]

isKnownUniquelyReferenced를 활용하면, 참조가 여러 개 존재하는 경우에만 복사를 수행하므로 불필요한 복사를 방지할 수 있습니다.

Copy-on-Write를 고려한 struct 디자인

  • 값 타입을 사용하지만, 참조 타입을 내부적으로 포함하여 COW를 적용
  • 불필요한 복사를 줄이기 위해 메모리 공유를 적극 활용

예제: COW 적용한 이미지 캐싱

class ImageData {
    var data: Data
    init(_ data: Data) { self.data = data }
}

struct Image {
    private var storage: ImageData
    
    init(data: Data) {
        self.storage = ImageData(data)
    }
    
    mutating func editImage(newData: Data) {
        if !isKnownUniquelyReferenced(&storage) {
            storage = ImageData(newData) // 복사 수행
        } else {
            storage.data = newData // 기존 데이터 수정
        }
    }
}

위와 같은 방법을 사용하면 큰 데이터(예: 이미지, 오디오 파일 등)를 값 타입으로 다룰 때 불필요한 복사를 방지할 수 있습니다.

 

5. Copy-on-Write의 장점

메모리 효율성

  • 변경이 필요할 때만 복사되므로, 불필요한 메모리 사용을 줄일 수 있음

성능 향상

  • 복사가 최소화되므로, 값 타입을 사용해도 성능 저하가 발생하지 않음

스레드 안전성

  • 값 타입을 사용하면 기본적으로 스레드 안전(thread-safe)한 설계를 유지할 수 있음

 

Swift의 Copy-on-Write는 값 타입의 메모리 복사 비용을 최소화하면서도 참조 타입의 장점을 활용하는 최적화 기법입니다. Swift의 컬렉션 타입(Array, Dictionary, Set, String)을 사용할 때 기본적으로 적용되며, isKnownUniquelyReferenced를 활용하면 사용자 정의 타입에서도 효율적인 Copy-on-Write 설계를 구현할 수 있습니다.

반응형