Struct
- stack 영역을 사용한다. Heap을 쓰지 않으므로 Reference couting 이 필요하지 않음
- 다른 변수에 할당한 경우 값 전체가 복사가 되며, 할당된 값을 변경하더라도 기존의 값이 변경되지 않는다.
- 따라서 Thread 의 의도치 않은 공유로부터 안전하다.
- Identity가 아닌 Value 자체가 중요하다.
- 그렇다면 성능은?
- 내부 데이터(property)가 만약 Heap 과 혼용되는 경우는 결국 값의 복사 + 레퍼런스도 카피가 된다.
- 이때 말하는 내부 데이터를 예를 들자면 string, array, set, dictionary
- 또한 COW(Copy On Write) 를 통해서 불필요한 작업을 줄임
- 만약 immutable 하게 만들면 어떤가?
- 실행할때 새로운 객체를 만드는 것은 비효율적이다. mutable + value type의 경우 값을 변경하면 되는데 immutable + reference 의 경우 객체를 생성해서 넘겨야하는 경우가 있다면. 변경하지 않아도 되는 값 마저 다 생성해서 넘기는 것이 맞는 것인가? 에 대한 생각이 든다.
- 그렇다면 성능은?
Class
- 언제쓰는가? Value 자체보다 Identity가 더 중요한 경우가 있다.
- 대표적으로 UIView 를 생각해보자.
성능에 영향을 미치는 3가지
- stack vs heap
- reference counting
- method dispatch > static vs dynamic
stack vs heap - Heap 할당 줄이기
- Heap 할당이 성능에 영향을 미치는 요소는 다음이 포함되어 있다.
- 비워진 공간을 찾고 원하는 만큼 할당시켜야 하며, 이를 관리하는 것이 복잡하다.
- 동시에 thread safe 해야하기 때문에 lock synchroniztion 동작등이 성능에 큰 영향을 미친다.
- 예를 들어 Dictionary 의 key값을 설정할때 string 을 사용하지 말고 struct를 활용해보자
- string을 사용하는 경우 변수에 할당할때 heap 에 저장하게 된다. 만약 loop 내부에서 돌게 된다면???
- 따라서 value type인 struct를 써보자 이때 hashable 만 채택하면 된다.
reference counting
- 정말 자주 사용된다. 또한 성능에 영향을 미치는 큰 이유중 하나가 thread safe 해야하기 때문
- 따라서 최대한 줄이는 것이 좋다. 위와 마찬가지로 loop 내부에 있다면…?? value sementics에 비해 성능이 좋지 못할 것
Method Dispatch
- static
- 컴파일 시점에 메소드의 실제 코드 위치를 안다면 실행중 찾는 과정 없이도 해당 코드 주소로 점프가 가능함.
- 컴파일러 최적화 및 메소드 인라이닝 가능
- 메소드 인라이닝이란, 컴파일 시점에 메소드 호출부도 같이 넣은것 처럼 동작하는 것
- 이로 인해 call stack overhead 가 줄어들고 루프 안에서 호출하는 경우는 큰 효과를 누릴 수 있음
- dynamic
- static 과 다르게 컴파일 시점에는 어디에 있는 메소드가 호출되는지 모른다. 따라서 런타임시 이를 찾아내야함. 이로 인해 컴파일러 최적화가 어려움.
- 따라서 static 으로 강제하기 위해서 final, private 의 사용을 습관화 해야함
- Xcode : Whole Module Optimization 옵션 사용할 것
추상화 기법들
- Class 는 성능 안좋은 것은 다 가지고 있다.
- 그나마 개선이 가능한 부분은 final 을 통한 static dispatch
- Struct 를 사용하는데 내부에 Reference Sementics 가 많다면??
- 값을 복사한다고 가정했을 때 내부의 reference Type 만큼 reference counting 이 일어나게 됨. 이는 성능에 영향을 미친다.
- 참고로 String, Array, Dictionary 도 Struct 이지만 내부적으로는 reference 를 사용하고 있다.
- 따라서 줄이기 위해서 enum 등을 사용하고, 가능하다면 reference type들을 묶을 수 있는 방법을 생각해보자.
- Protocol 은 Value Type에서도 다형성을 구현할 수 있는 큰 장점이 있다.
- 의문점 1 : 어떤 타입이 들어갈지 모르는데 사이즈를 어떻게 계산해 놓는가?
- 일정 크기를 넘어가면 내부적으로 heap을 사용하고, 실제로는 주소만 갖고 있다. 프로토콜의 실제 값을 관리하는 Existential Container를 통해 이뤄진다.
- 이렇게 일정 크기를 판단하고 Existential Container를 관리하는 주체는 VWT ( Value Witness Table ) 라고 한다. 이는 각 프로토콜 마다 존재한다.
- 의문점 2 : 어떻게 적절한 함수를 호출하는가?
- PWT ( Protocol Witness Table )이 있다. V-table과 유사함.
- 위에서 설명한 VWT, PWT 는 Extential Containor 내부에서 참조할 수 있다.
- 다시한번 볼것 VWT, PWT 차이점 등등
- 만약 큰 값을 가진 Protocol 을 복사한다면?
- 위에서 말한것 처럼 Heap을 사용하는 protocol을 복사하게 되면 heap을 또 사용해야한다. 즉, 복사를 할때마다 heap 을 할당해야함. 이는 비 효율적
- 이는 COW ( Copy On Write ) 를 통해서 개선하게 되었다.
- 의문점 1 : 어떤 타입이 들어갈지 모르는데 사이즈를 어떻게 계산해 놓는가?
- Generic
- protocol 과 비슷하게 VWT, PWT 로 구현이 되어있다.
- Generic 특수화 : 여러 타입을 대응하려고 Generic을 만들었지만, 만약에 성능을 생각해서 각 타입으로 나눠서 쓴다면 메소드 인라이닝이 가능해져서 성능이 좋아질 것이다. > 성능을 생각하면 Generic을 쓰면 안되는가?? > WMO가 이를 대신 해준다.
- 이런 특수화는 정적 다형성(Static Polymorphism) 덕분에 가능하다. > 컴파일 시점에 부르는 곳 마다 타입이 정해져 있고, 런타임시 바뀌지 않으며, 특수화가 가능하다.
Reference
https://www.youtube.com/watch?v=z1Gf6EosaUQ
- 발표자님께서 참고하신 WWDC
- WWDC 2016 : S416 : Understanding Swift Performance
- WWDC 2015 : S409 : Optimizing Swift Performance
- WWDC 2015 : S414 : Building Better Apps With Value Types in Swift
'iOS & Swift' 카테고리의 다른 글
UICollectionViewCompositionalLayout (0) | 2022.10.31 |
---|---|
UINib Basic (3) | 2021.10.04 |
Property Wrapper (0) | 2021.06.07 |
Apple 공식 Guide & Reference 모음 (0) | 2021.05.21 |
Escaping Closure (0) | 2021.05.16 |