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가지

  1. stack vs heap
  2. reference counting
  3. 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 ) 를 통해서 개선하게 되었다.
  • 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

+ Recent posts