Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
Tags
- appstore
- Apple
- iOS16
- IOS
- geofencing
- 한글
- darkmode
- JPA
- Firebase
- FLUTTER
- mac
- Swift
- Notification
- Session
- Archive
- Xcode
- 웹뷰
- stack
- Git
- Code
- view
- MacOS
- UIButton
- SwiftUI
- error
- window
- rxswift
- 개발자
- github
- Realm
Archives
- Today
- Total
EEYatHo 앱 깎는 이야기
Swift ) Class vs Struct 성능 비교 - EEYatHo iOS 본문
반응형
성능 비교의 3가지 관점
- Allocation: 인스턴스를 생성하면 Stack과 Heap 중 어느 곳에 할당 되는 지
- Reference Counting: 인스턴스를 통해 레퍼런스 카운트가 몇개가 발생하는지
- Method Dispatch: 인스턴스에서 메소드를 호출했을 때, 메소드 디스패치가 정적인지 동적인지
Allocation
- Stack 은 LIFO 구조 및 Pointer 를 이용해서 할당, 해제하기에 간단한 만큼, O(1)의 빠른 속도를 가짐
- Heap 은 사용하지 않은 블럭을 찾아서 할당하고, 해제한 블럭을 적절한 곳에 재삽입해야하고,
thread-safe 를 위해 locking 또는 기타 동기화 기법을 사용해 무결성을 보호해야하기에, Stack 보다 느린 속도를 가짐 - Stack 은 Value-Sementics 를 할당하며, struct, enum, tuple, 기본타입들이 있다.
- Heap 은 Reference-Sementics 를 할당하며, class, closure 이 있다.
- struct 는 stack 에, class 는 Heap 에 할당되기 때문에 struct 의 승리
- Heap Allocation 피하기
- String 은 struct 지만, 실질적으로는 character 들을 heap 에 간접적으로 저장함
원문 및 WWDC링크 : Also, String can represent so many things because it actually stores the contents of its characters indirectly on the heap.
- 하지만 짧은 String 은 Stack 에 저장될 수 있음
String 구현 github 링크 의 428줄의 주석 : "Small strings can by spilling to the stack"
- 하지만 짧은 String 은 Stack 에 저장될 수 있음
- String 및 class 들의 사용을 피하고, 가능하면 enum 과 struct 로 대체하여 사용하기
- String 은 struct 지만, 실질적으로는 character 들을 heap 에 간접적으로 저장함
Reference Counting
- Heap 에 있는 인스턴스는, 해제할 타이밍을 알기위해 자신을 참조하는 인스턴스들의 갯수를 표시하며 이를 레퍼런스 카운트라 함
- 레퍼런스 카운트는 인스턴스 자신이 가지고 있음
- 레퍼런스를 증감시키는 것은, Heap 할당과 마찬가지로 무결성을 보호하기 위한 오버헤드가 있음
레퍼런스 카운트가 필요없는 Stack 을 사용하는 Value-Sementics 들이 좋고, struct의 승리
- One more Step
- struct 지만, 내부적으로 class 들을 사용하면, 그만큼 Reference Counting 이 발생함
( struct 안에 2개의 class 인스턴스가 있다면, Reference Counting 2개가 발생 ) - 즉, struct 라도 class 보다 더 많은 Reference Counting 을 발생시킬 수 있음
( 2개의 class 인스턴스를 가진 struct 와, 2개의 struct 인스턴스를 가진 class 중
struct 가 Reference Counting 이 더 많다 )
- struct 지만, 내부적으로 class 들을 사용하면, 그만큼 Reference Counting 이 발생함
- 내 생각 : 많은 n개의 참조를 가진 데이터는 class 가 좋을 수 있다
- 처음엔 당연히 모든걸 struct 로 구현하면 좋겠는데?
struct 안에 n개의 class 인스턴스라면, Reference Counting n개 발생,
class 안에 n개의 class 인스턴스라면, Reference Counting n + 1개가 발생하니까?!
-> 해당 인스턴스를 복사할 때 class 가 더 났다.
class 는 Reference Counting 1개가 발생하고,
struct 는 Reference Counting n개가 발생하기 때문
- 처음엔 당연히 모든걸 struct 로 구현하면 좋겠는데?
Method Dispatch
- 어떤 메소드를 호출할지 결정하여, 그 메소드를 호출하는 과정
- 어떤 메소드인지 결정되는 시점에 따라 static dispatch 와 dynamic dispatch 로 나뉘게됨
- static dispatch
- 컴파일타임에 어떤 구현을 실행할지 결정할 수 있음
- 컴파일시 메소드 인라이닝 (Method Inlining) 과 같은 코드 최적화를 시행
- 메소드 인라이닝 : 메소드 호출부를 메소드 구현부로 대치시켜서 호출 과정을 생략하는 최적화 기법
- dynamic dispatch
- 런타임에 어떤 구현을 실행할지 결정할 수 있음
- 어떤 메소드를 실행할지 결정하고 해당 메소드로 jump 하여 코드 실행
- 컴파일러의 가시성(visibility)을 막기 때문에, 최적화에 악영향
- static dispatch
- dynamic dispatch 를 쓰는 이유 : 다형성 (polymorphism) 이라는 강력한 기능을 위해
- 아래의 코드에서 forEach 안의 구문이 어떤 int, myFunction 을 실행해야하는지 컴파일 타임에는 알 수 없음
class A {
var int: Int = 0
func myFunction() {}
}
class B: A {
var _int: Int = 0
override var int: Int {
get { _int * 2 }
set { _int = newValue * 2 }
}
override func myFunction() {}
}
class C: A {
var _int: Int = 0
override var int: Int {
get { _int * 3 }
set { _int = newValue * 3 }
}
override func myFunction() {}
}
let a = A()
let b = B()
let c = C()
let arr: [A] = [a, b, c]
arr.forEach {
$0.int = 2
$0.myFunction()
}
- dynamic dispatch는 해당 타입의 vtable에 접근하여 정확한 프로퍼티, 메소드에 접근
vtable 설명 링크
원문 : A virtual method table or 'vtable' is a type specific table referenced by instances that contains the addresses of the type's methods. Dynamic dispatch proceeds by first looking up the table from the object and then looking up the method in the table.
$0.type.vtable.int = 2 // $0.int = 2
$0.type.vtable.myFunction() // $0.myFunction()
- class 에서 dynamic dispatch 피하기
- class 에 final 구문을 사용하면, 해당 클래스의 프로퍼티와 메소드는 더이상 상속하지 않기에,
컴파일러가 static dispatch 를 진행하고 최적화를 진행할 수 있음 - 이는 프로퍼티, 메소드들에게 각각 적용
class가 final이 아니어도, 메소드에 final을 붙히면 static dispatch를 진행 - 참고 링크
- class 에 final 구문을 사용하면, 해당 클래스의 프로퍼티와 메소드는 더이상 상속하지 않기에,
- 따라서
class 는 다형성 때문에 dynamic dispatch
final class, struct 는 더이상 상속하지않기에 static dispatch
로 나눌 수 있다
Reference
'iOS, Swift > Swift Theory' 카테고리의 다른 글
Swift ) async/await - EEYatHo iOS (0) | 2023.01.16 |
---|---|
Swift ) Concurrency, Thread, GCD - EEYatHo iOS (0) | 2023.01.12 |
Swift ) Dynamic Image - EEYatHo iOS (1) | 2022.10.15 |
Swift ) 고차함수 - EEYatHo iOS (2) | 2022.09.23 |
Swift ) 유니코드, 한글, 정규화 - EEYatHo iOS (0) | 2022.03.29 |
Comments