Swift

[Swift] Swift Performance (1) | swift에서 성능을 좌우하는 3가지 요소와 구조체의 성능 변화

하이D:) 2024. 2. 1. 09:46

우리가 앱을 만들면서 성능에 영향을 주는 요소들은 수 없이 많겠지만 Apple이 WWDC에서 발표한 Swift의 성능에 영향을 미치는 3가지 요인에 대해서 알아보고 그동안 가볍게 쓸 수 있다고 믿었던 struct가 프로토콜을 채택함으로써 성능이 어떻게 변화하는지를 알아보자!

 

 

 

Swift의 성능에 영향을 미치는 3가지 요인

Apple이 WWDC에서 발표한 Swift의 성능에 영향을 미치는 3가지 요인은 다음과 같다.

 

Allocation

  • 인스턴스의 메모리 위치 (Stack or Heap)
  • Stack에 위치할 경우 성능에 유리하다

 

Reference Counting

  • 인스턴스가 참조될 시, 참조를 카운팅 하는지.
  • ( 참조 카운팅을 하는 경우, 참조 카운팅이 0이 되면 메모리에서 해제하며 메모리 누수 발생을 조심해야 한다. )

 

Method Dispatch

  • 메서드 실행방식이 Static(Direct)방식인지 Dynamic(Table) 방식인지.
  • Static(Direct) 방식일 경우에 성능에 유리하다.

 

 

 

Struct와 Class의 성능 차이

우리가 가장 자주 접하는 단순한 형태의 구조체와 클래스의 성능은 어떨까?

 

Struct

  • 인스턴스의 값을 Stack에 할당
  • Heap에 동적으로 할당되지 않으니 Reference Counting을 수행하지 않음
  • Struct는 상속이 불가능하여 별도의 V-Table을 가지고 있지 않기 때문에 컴파일 시점에 Static Method Dispatch를 수행합니다.

 

 

Class

  • 인스턴스의 값을 Heap에 동적 할당
  • 인스턴스의 수명을 관리하기 위해 Reference Counting을 수행
  • 상속이 가능하여 V-Table을 가지고 있기에 런타임에서 Dynamic Method Dispatch를 수행합니다.

 

 

 

Struct + Protocol의 성능

구조체와 클래스의 비교에서 볼 수 있듯이 일반적으로는 struct를 사용하는 게 성능면에서 유리하다고 보인다. 하지만 우리는 struct 단일로 코드를 작성하기도 하지만 protocol과 함께 사용하여 다형성을 구현할 수도 있다.

 

struct + protocol는 왜 필요하고 이 경우에 어떻게 값을 저장하고 메서드를 실행하는지에 대해서는 아래 글들에서 살펴보았다.

 

Swift의 메서드 실행 방법 (2) | table 기반 메커니즘 Virtual Table과 Witness Table

Swift의 메서드 실행 방법 (3) | 프로토콜 채택한 구조체 Existential Container / ValueBuffer / VWT / PWT

 

 

단순한 struct와 다르게 프로토콜 타입의 struct의 성능은 어떻게 달라졌을지 알아보자

Small Struct + Protocol

  • 작은 메모리 크기의 struct의 경우 저장 프로퍼티의 값이 value buffer용량을 초과하지 않기 때문에 stack 영역의 existential container 내부에 저장되며
  • protocol witness table을 이용해서 런타임에서 메서드를 찾아가는 과정이 생겨 struct이긴 하지만 Dynamic Method Dispatch 방식으로 메서드 디스패치한다.

 

Large Struct + Protocol

  • 큰 메모리 크기의 struct의 경우 저장 프로퍼티 값이 value buffer를 초과하는 struct의 경우에는 heap영역에 저장되고
  • 그로 인해서 Reference Counting까지 수행한다.
  • 이 경우에도 마찬가지로 protocol witness table을 이용해서 런타임에서 메서드를 찾아가는 과정이 있기 때문에 Dynamic Method Dispatch 방식으로 메서드 디스패치한다.

 

 


 

이번 글에서 알 수 있듯이 같은 struct라고 해서 동일한 성능을 가지는 게 아니라 특정 상황에서는 성능이 저하될 수 있다. 그래서 성능이 중요한 시점에서는 이런 것도 하나하나 따져보아야 하는 것 같다.

다음 글에서는 제네릭의 신비함을 살펴볼 건데 제네릭을 사용했을 때 가능한 static polymorphism(parametric polymorphism)specializarion을 통해 다시 성능 좋아지도록 하는 방법이 있다!