iOS/🟠 Swift

COW(Copy-on-Write) λ₯Ό μ•Œμ•„λ³΄μž

woozzang 2022. 12. 29. 23:34

2022λ…„μ˜ λ§ˆμ§€λ§‰ λΈ”λ‘œκ·Έ 글이 될 것 κ°™λ‹€

 

Copy-on-Write

값을 μ‹€μ œλ‘œ μ“Έ λ•Œ (on-write) λ³΅μ‚¬ν•œλ‹€(copy)

 

μ‹€μ œ 볡사λ₯Ό λŠ¦μΆ”μ–΄ νž™ 할당을 μ€„μ΄λŠ” μ΅œμ ν™” 방법

 

 

Usecases

1. Swift Collection : Array, Dictionary, String λ“±

 

2. 3-word κ°€ λ„˜λŠ” struct λ₯Ό Protocol νƒ€μž…μœΌλ‘œ λ‹€λ£° λ•Œ 

 

 

κ°’ νƒ€μž…κ³Ό νž™ μ˜μ—­μ˜ 관계

Array λ‚˜ String 은 μŠ€μœ„ν”„νŠΈμ—μ„œ κ°’ νƒ€μž… μž…λ‹ˆλ‹€.

 

κ°’ νƒ€μž… μΈμŠ€ν„΄μŠ€λŠ” 기본적으둜 μŠ€νƒ μ˜μ—­μ„ ν• λ‹Ήν•˜κ³  κ·Έ 곳에 값을 μ΄ˆκΈ°ν™”ν•˜λŠ” κ²ƒμœΌλ‘œ μ•Œλ €μ Έ μžˆμŠ΅λ‹ˆλ‹€.

 

그러면 κ°’ νƒ€μž…κ³Ό νž™ μ˜μ—­μ€ μ–΄λ–€ 관련이 μžˆμ„κΉŒμš”?

 

Arrray, String λ“± Collection 을 ν™œμš©ν•˜λŠ” νŠΉμ • κ°’ νƒ€μž…λ“€μ€ λŸ°νƒ€μž„에 element 의 μΆ”κ°€, μ‚­μ œκ°€ κ°€λŠ₯ν•˜κΈ° λ•Œλ¬Έμ—νž™ μ˜μ—­ 데이터λ₯Ό μœ„ν•œ 곡간을 λ‚΄λΆ€μ μœΌλ‘œ ν• λ‹Ήν•©λ‹ˆλ‹€.

 

그런데 이 νƒ€μž…λ“€μ€ κ°’ νƒ€μž…μ΄κΈ° λ•Œλ¬Έμ— value-semantic 을 κ°€μ Έμ•Όν•©λ‹ˆλ‹€.

 

value-semnantic 의 νŠΉμ§•μ€ 볡사할 λ•Œλ§ˆλ‹€ κΉŠμ€ 볡사가 μΌμ–΄λ‚˜λŠ” κ²ƒμž…λ‹ˆλ‹€.

 

 μœ„ 사싀 λ•Œλ¬Έμ— 이 νƒ€μž…λ“€μ€ 값을 볡사할 λ•Œλ§ˆλ‹€ νž™ 할당이 μΌμ–΄λ‚˜μ•Όν•©λ‹ˆλ‹€.

 

κ°’ νƒ€μž…μ˜ νŠΉμ„± λ•Œλ¬Έμ—, μΈμŠ€ν„΄μŠ€λ§ˆλ‹€ 독립적인 데이터λ₯Ό 가지고 μžˆμ–΄μ•Όν•˜κΈ° λ•Œλ¬Έμž…λ‹ˆλ‹€.

 

 

νž™ ν• λ‹Ήκ³Ό μ„±λŠ₯

νž™ 할당은 κ°€λŠ₯ν•œ κ·Έ 횟수λ₯Ό μ€„μ—¬μ•Όν•˜λŠ” μ„±λŠ₯ μ €ν•΄ μš”μ†Œ 쀑 ν•˜λ‚˜μž…λ‹ˆλ‹€.

 

Arrray, String λ“± Collection 을 ν™œμš©ν•˜λŠ” νŠΉμ • κ°’ νƒ€μž…λ“€μ€ 볡사 μ‹œ λ°œμƒν•˜λŠ” νž™ 할당을 쀄이기 μœ„ν•΄ COW λ₯Ό μ‚¬μš©ν•©λ‹ˆλ‹€.

 

COW λŠ” κ°’μ˜ μˆ˜μ • λ™μž‘ λ•Œ μ‹€μ œ 볡사가 μΌμ–΄λ‚˜μ„œ 결과적으둜 νž™ ν• λ‹Ή 수λ₯Ό μ€„μ΄λŠ” μ„±λŠ₯ μ΅œμ ν™” κΈ°μˆ μž…λ‹ˆλ‹€.



COW λ₯Ό μ‚¬μš©ν•˜λ©΄ κ°’ νƒ€μž…μ˜ μž₯점 ( No unintended sharing of state ) 을 κ°€μ Έκ°€λ©΄μ„œ value-semantic λ•Œλ¬Έμ— μƒκΈ°λŠ” λΆˆν•„μš”ν•œ νž™ 할당을 쀄일 수 μžˆμŠ΅λ‹ˆλ‹€.

 

이 κΈ€μ—μ„œλŠ” νž™ 할당이 μ„±λŠ₯에 μ–΄λ–€ 영ν–₯을 μ£ΌλŠ”μ§€ λ‹€λ£¨μ§€λŠ” μ•ŠμŠ΅λ‹ˆλ‹€.

 

κ΄€λ ¨ λ‚΄μš©μ€ WWDC 16: Understanding Swift Performance μ˜μƒμ„ μ°Έκ³ ν•˜μ‹œλ©΄ 쒋을 것 κ°™μŠ΅λ‹ˆλ‹€.

 

 

νž™ μ£Όμ†Œλ₯Ό ν™•μΈν•΄μ„œ COW κ°€ μΌμ–΄λ‚˜λŠ”μ§€ 증λͺ…ν•˜κΈ°

μ–΄λ–€ νƒ€μž…μ΄ COW κ°€ μΌμ–΄λ‚œλ‹€κ³  ν•  λ•Œ, λ‹€μŒκ³Ό 같이 λ™μž‘ν•¨μ„ μ˜ˆμƒν•  수 μžˆμŠ΅λ‹ˆλ‹€.


1. 볡사 λ™μž‘ μ‹œμ—λŠ” λ™μΌν•œ νž™ μ˜μ—­μ„ κ³΅μœ ν•œλ‹€.


2. μˆ˜μ • λ™μž‘μ΄ 일어날 λ•Œ 독립적인 νž™ μ˜μ—­μ„ ν• λ‹Ή/ μ΄ˆκΈ°ν™”ν•˜κ³  μˆ˜μ •μ‚¬ν•­μ„ λ°˜μ˜ν•œλ‹€.


κ°„λ‹¨ν•œ μ½”λ“œλ‘œ 보면 λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

 

var array1 = [1,2,3]

var array2 = array1 // 볡사 λ™μž‘, 이 λ•ŒλŠ” 같은 νž™ μ˜μ—­μ„ κ³΅μœ ν•œλ‹€.

array2.append(4) // μˆ˜μ • λ™μž‘, array1 κ³Ό λ‹€λ₯Έ 독립적인 νž™ μ˜μ—­μ„ μ‚¬μš©ν•œλ‹€.

 

이것을 증λͺ…ν•  λ•Œμ˜ 핡심은 μΈμŠ€ν„΄μŠ€κ°€ μ‚¬μš©ν•˜κ³  μžˆλŠ” νž™ μ˜μ—­μ˜ 포인터λ₯Ό μ–»μ–΄μ•Όν•œλ‹€λŠ” κ²ƒμž…λ‹ˆλ‹€.
κ°’ νƒ€μž…, μ°Έμ‘° νƒ€μž…κ³Ό 관련없이 μŠ€νƒ μ˜μ—­μ€ μΈμŠ€ν„΄μŠ€λ§ˆλ‹€ 항상 λ…λ¦½μ μœΌλ‘œ ν• λ‹Ήλ˜κΈ° λ•Œλ¬Έμ—,
μŠ€νƒ μ˜μ—­ μ£Όμ†Œλ₯Ό ν™•μΈν•˜λŠ” 것은 μ˜λ―Έκ°€ μ—†μŠ΅λ‹ˆλ‹€.

κ·Έλ ‡λ‹€λ©΄ ν¬μΈν„°λŠ” μ–΄λ–»κ²Œ 얻을 수 μžˆμ„κΉŒμš”?
μš°μ„  ν¬μΈν„°λŠ” μ£Όμ†Œλ₯Ό μ €μž₯ν•  수 μžˆλŠ” λ³€μˆ˜μž…λ‹ˆλ‹€.
기본적으둜 νŠΉμ • κ°’μ—μ„œ μ–»μ–΄λ‚Έ ν¬μΈν„°λŠ” 값이 ν• λ‹Ήλœ λ©”λͺ¨λ¦¬μ˜ μ‹œμž‘μ£Όμ†Œλ₯Ό λ‹΄κ³  μžˆμŠ΅λ‹ˆλ‹€.
C 포인터λ₯Ό μ΄ν•΄ν•˜κΈ° μœ„ν•΄ κ°„λ‹¨ν•˜κ²Œ μ •λ¦¬ν•œ λ…Έμ…˜ 링크λ₯Ό μ²¨λΆ€ν•©λ‹ˆλ‹€. (링크)

μŠ€μœ„ν”„νŠΈλŠ” ARC λ₯Ό 톡해 λ©”λͺ¨λ¦¬λ₯Ό κ΄€λ¦¬ν•˜κ³  있고, λ©”λͺ¨λ¦¬ μ•ˆμ •μ„±μ„ μœ„ν•΄ 포인터 μ‚¬μš©μ„ μ§€μ–‘ν•©λ‹ˆλ‹€.
ν•˜μ§€λ§Œ μŠ€μœ„νŠΈν”„λŠ” C μ–Έμ–΄μ™€μ˜ μƒν˜Έμš΄μš©μ„±μ„ μœ„ν•΄ Unsafe, Unmanaged λΌλŠ” prefix 둜 μ‹œμž‘ν•˜λŠ” API λ₯Ό μ œκ³΅ν•©λ‹ˆλ‹€.
그리고 μŠ€μœ„ν”„νŠΈλŠ” μŠ€μœ„ν”„νŠΈ 값을 ν¬μΈν„°λ‘œ μžλ™μœΌλ‘œ λ³€ν™˜ν•΄μ£ΌλŠ” νŽΈλ¦¬ν•œ κΈ°λŠ₯을 μ œκ³΅ν•©λ‹ˆλ‹€.
μ €λŠ” 이 κΈ°λŠ₯을 ν™œμš©ν•˜μ—¬ μΈμŠ€ν„΄μŠ€μ˜ 포인터λ₯Ό μ–»μ–΄μ„œ μ£Όμ†Œλ₯Ό 확인할 κ²ƒμž…λ‹ˆλ‹€.

WWDC 20 Unsafe Swift 쀑

 

주어진 μ£Όμ†Œκ°€ νž™ μ˜μ—­μΈμ§€ μŠ€νƒ μ˜μ—­μΈμ§€ μ•Œ 수 μžˆλŠ” 방법

zsh λͺ…λ Ήμ–΄λ₯Ό 톡해 ν”„λ‘œμ„ΈμŠ€μ˜ λ©”λͺ¨λ¦¬ μ˜μ—­μ΄ μ–΄λ–»κ²Œ κ΅¬μ„±λ˜μ–΄μžˆλŠ”μ§€ ν™•μΈν•˜κ³ ,
주어진 μ£Όμ†Œκ°€ μ–΄λ–€ μ˜μ—­μ— ν¬ν•¨λ˜λŠ”μ§€ νŒλ‹¨ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

νž™ μ˜μ—­
vmmap <PID> | grep Malloc
MALLOC_TINY, MALLOC_SMALL, MALLOC_NANO λ“±μ˜ μ΄λ¦„μœΌλ‘œ ν• λ‹Ή 된 것을 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.

 



μŠ€νƒ μ˜μ—­

vmmap <PID> | grep Stack
thread λ§ˆλ‹€ μŠ€νƒ μ˜μ—­μ΄ ν• λ‹Ήλ˜μ—ˆκ³  각 μ˜μ—­μ˜ μ£Όμ†Œ λ²”μœ„λ₯Ό 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.

 

μ£Όμ†Œλ₯Ό μ–»λŠ” λ©”μ„œλ“œ κ΅¬ν˜„

func address<T>(of: UnsafePointer<T>) {
      
    let result = String(format: "%018p", of)
    
    print(result)
}

/// String νž™ μ˜μ—­ 접근을 μœ„ν•΄μ„œ ν•„μš”!!

func address(with: UnsafePointer<CChar>) {

    let result = String(format: "%018p", with)
    
    print(result)
}

 

Array ν™•μΈν•˜κΈ°

UnsafePointer<Int> 둜 λ³€ν™˜ν•˜λ©΄ νž™ μ˜μ—­ μ£Όμ†Œλ₯Ό κ°€μ Έμ˜¬ 수 μžˆμŠ΅λ‹ˆλ‹€.
* μΆ”κ°€ 정보: Array<T> λŠ” μŠ€νƒ λ‚΄ ν• λ‹Ήλ˜λŠ” μ‚¬μ΄μ¦ˆκ°€ 항상 8λ°”μ΄νŠΈμ˜€μŠ΅λ‹ˆλ‹€. (레퍼런슀만 μ €μž₯함을 μœ μΆ”ν•  수 μžˆμŠ΅λ‹ˆλ‹€)


확인 κ²°κ³Ό Array λŠ” COW 이 μž‘λ™ν•˜λŠ” 것을 확인할 수 μžˆμ—ˆμŠ΅λ‹ˆλ‹€.

 

String ν™•μΈν•˜κΈ°

이번 ν•™μŠ΅μ„ 톡해 μ•Œκ²Œλœ μ‚¬μ‹€μž…λ‹ˆλ‹€.
String 은 포인터 νƒ€μž…μ— 따라 λ‹€λ₯Έ μ£Όμ†Œλ₯Ό κ°€μ Έμ˜΅λ‹ˆλ‹€.
UnsafePointer<String> 둜 λ³€ν™˜ν•˜λ©΄ μŠ€νƒ μ˜μ—­ μ£Όμ†Œλ₯Ό κ°€μ Έμ˜΅λ‹ˆλ‹€. (const String *)
UnsafePointer<CChar> 둜 λ³€ν™˜ν•˜λ©΄ λ‚΄λΆ€μ μœΌλ‘œ κ΄€λ¦¬ν•˜λŠ” νž™ μ˜μ—­ μ£Όμ†Œλ₯Ό κ°€μ Έμ˜΅λ‹ˆλ‹€. (const char *)
* μ—¬κΈ°μ„œ char * λŠ” C μ—μ„œ λ¬Έμžμ—΄ 포인터에 ν•΄λ‹Ήν•©λ‹ˆλ‹€.

 

확인 κ²°κ³Ό String 은 COW 이 μž‘λ™ν•˜λŠ” 것을 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.

* 짧은 λ¬Έμžμ—΄μ˜ 경우 μŠ€νƒ μ˜μ—­λ§Œ μ‚¬μš©ν•˜κΈ° λ•Œλ¬Έμ— 이 λ•Œ μ–»λŠ” νž™ μ£Όμ†ŒλŠ” μ˜λ―Έμ—†λŠ” κ°’μœΌλ‘œ νŒλ‹¨ν•˜μ˜€μŠ΅λ‹ˆλ‹€.

 

직접 COW λ₯Ό κ΅¬ν˜„ν•˜κΈ°

 

μ•„λž˜ 링크에 μžμ„Ένžˆ λ‚˜μ™€μžˆμ–΄ μžμ„Έν•œ λ‚΄μš©μ€ μƒλž΅ν•©λ‹ˆλ‹€.
κ΅¬ν˜„μ˜ 핡심은 struct 이 μ°Έμ‘°νƒ€μž… 속성을 톡해 indirect storage λ₯Ό 가지고,
볡사 μ‹œ λ™μΌν•œ μ°Έμ‘°νƒ€μž… μΈμŠ€ν„΄μŠ€λ₯Ό κ³΅μœ ν•©λ‹ˆλ‹€.
이 ν›„ 값을 μˆ˜μ •ν•  μ‹œ `isKnownUniquelyReferenced()` API λ₯Ό 톡해 μ°Έμ‘°νƒ€μž… μΈμŠ€ν„΄μŠ€μ˜ μ°Έμ‘° 개수λ₯Ό νŒŒμ•…ν•©λ‹ˆλ‹€.
λ§Œμ•½ 1개 이상일 경우 κ·Έ λ•Œμ„œμ•Ό 독립적인 볡사본 μΈμŠ€ν„΄μŠ€λ₯Ό λ§Œλ“€κ³  κ·Έ 값을 μˆ˜μ •ν•©λ‹ˆλ‹€.

 

GitHub - apple/swift: The Swift Programming Language

The Swift Programming Language. Contribute to apple/swift development by creating an account on GitHub.

github.com

 

같이 보면 쒋은 μ˜μƒ

μ•„λž˜λŠ” μœ„ λ‚΄μš©μ„ 잘 μ΄ν•΄ν•˜κΈ° μœ„ν•΄ 같이 보면 쒋은 μ˜μƒλ“€μž…λ‹ˆλ‹€.
μ˜μƒμ„ 직접 보고 ν•™μŠ΅ν•˜λŠ”κ²ƒμ΄ κ°€μž₯ μ’‹μŠ΅λ‹ˆλ‹€.
λ―Έν‘ν•˜μ§€λ§Œ μ œκ°€ μ •λ¦¬ν•œ λ…Έμ…˜ 링크λ₯Ό μΆ”κ°€λ‘œ λ‚¨κΉλ‹ˆλ‹€.

WWDC 16 Understatnding Swift Performance [ μ˜μƒ 링크 , λ…Έμ…˜ 링크 ]
WWDC 18 iOS Memory Deep Dive [μ˜μƒ 링크, λ…Έμ…˜ 링크]
WWDC 20 Unsafe Swift [μ˜μƒ 링크, λ…Έμ…˜ 링크]

 

같이 보면 쒋은 κΈ€

 

 

μŠ€μœ„ν”„νŠΈ νƒ€μž…λ³„ λ©”λͺ¨λ¦¬ 뢄석 μ‹€ν—˜

struct와 class κ°€ λ©”λͺ¨λ¦¬ μ˜μ—­μ„ μ–΄λ””λ₯Ό μ‚¬μš©ν•˜λŠ”μ§€ λΆ„μ„ν•œ μ‹€ν—˜ κ²°κ³Ό

medium.com

 


Rome was not built in a day πŸ›

 

πŸ€– [Back to the Basics] πŸ’»
μ£Όλ‹ˆμ–΄ iOS 개발자 우짱의 기술 λΈ”λ‘œκ·Έμž…λ‹ˆλ‹€