1. Copy-on-Write(COW)란?
Copy-on-Write(COW)는 값 타입(Value Type)의 복사 비용을 최적화하기 위한 메모리 관리 기법입니다.
일반적으로 Swift에서 값 타입(예: struct, enum)은 참조가 아닌 복사를 통해 전달됩니다. 하지만, 값이 변하지 않는 한 굳이 복사할 필요가 없으므로, 공유된 데이터에 대한 변경이 일어나기 전까지 복사를 미루는 전략이 COW입니다.
2. Copy-on-Write의 동작 원리
Copy-on-Write는 참조 카운팅을 활용하여 공유된 데이터인지 확인하고, 변경이 발생할 때만 실제 복사를 수행하는 방식으로 동작합니다.
COW의 동작 과정
- 초기 상태:
- 새로운 변수에 값을 할당하면, 내부적으로 같은 메모리를 공유합니다.
- 참조 카운트가 증가하며, 원본 데이터를 그대로 사용합니다.
- 읽기(Read) 시:
- 값 타입이지만 변경되지 않는 경우, 같은 메모리를 계속 공유하여 성능을 최적화합니다.
- 쓰기(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]
위 코드에서 array1과 array2는 초기에 같은 메모리를 공유하지만, 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 설계를 구현할 수 있습니다.
'프로그래밍언어' 카테고리의 다른 글
[Swift] UnsafePointer, UnsafeMutablePointer, UnsafeRawPointer 차이점과 사용법 (0) | 2025.02.19 |
---|---|
[JAVA] Checked Exception에 대하여 (0) | 2025.02.17 |
[Swift] Actor와 Structured Concurrency (0) | 2025.02.16 |
[Swift] Distributed Actor (0) | 2025.02.16 |
[Swift] Swift의 유연한 문법 기능을 활용한 코드 설계 (0) | 2025.02.16 |