본문 바로가기
iOS/Swift

[Swift] Increasing Performance by Reducing Dynamic Dispatch

by Jiseong 2022. 4. 22.

많이들 고민하고 사용하는 Class..

클래스와 구조체는 할당부터 차이가 있지만 이번엔 Method Dispatch에 관련하여 알아보려 한다.

 

근데 Method Dispatch가 뭔데..?

디스패치하면 연예인들 찍는 게 생각날텐데, 직역한다면 메소드 부치기... 발송하기..? 

 

메소드를 어느 곳에 부칠지에 대한 의미가 어느정도 들어맞는 것 같다.

 

Method Dispatch란 특정 함수 호출시 어느 곳에서 함수를 호출할 것이며 어떤 함수를 호출할 것인지를 결정해주는 방식이라고 생각하면 될 것 같다.


우선 각 Dispatch 방식부터 간단히 알아보고 가자

이번 글은 클래스의 성능향상이니...

 

결론부터 말하자면 Method Dispatch 방식은 두가지로 나뉜다.

 

Static Dispatch

Dynamic Dispatch

 

Static Dispatch 

값타입(구조체, 열거형), 상속 불가능한 타입은 Static Dispatch로 동작한다.

 

위에서 말한 타입들은 상속이 불가능하기 때문에 메서드의 재정의 또한 불가능하다.

 

이런 점에 의해 해당 타입들의 메서드는 어디서 어떤 것이 호출될지 정해져있다(고정, Static) 

 

어디서 어떤 것이 호출될지 컴파일 타임에 정해져있기 때문에 런타임에 정해진 함수만 실행하면 되므로 성능에 좋은 영향을 미치게 된다.

 

이렇게 어떤 함수들이 호출될지를 컴파일타임에 해당 함수가 담긴 주소값을 알고 있기때문에 인라이닝 등의 최적화를 기대할 수 있다.

메서드 인라이닝(Method Inlining)은 메소드를 호출 할 때 해당 메소드로 이동하지 않고 메소드의 결과값을 바로 반환하여 성능을 향상 시키는 것, 그니깐 찾질 않는다는 것임

 

Dynamic Dispatch

보통의 객체지향을 따르는 언어들이 해당 방식을 많이 사용한다.

 

하지만 Dynamic DispatchStatic Dispatch와 달리 컴파일 타임에 실행할 메서드를 정해놓는게 아닌 런타임에 어떤 메서드를 실행할지 결정한다.

 

실제 앱 실행 중에 어느 곳에서 어떤 메서드를 실행할 것인지를 찾아야되기 때문에 오버헤드(메서드를 탐색하는 비용 등)가 발생해 성능상 문제가 발생한다.

 

여기서 궁금한 점이 생긴다.

아니 오버헤드 없이 빠른 놈 두고 얘를 왜 써서 성능을 높이려 하는데? 

이는 OOP의 특성때문이다.

 

OOP의 특성중 다형성이라는 특성때문인데, Swift의 Class는 상속을 지원한다.

 

클래스는 상속을 받을 수 있고, 상속을 할 수 도 있는데 상위 클래스의 메서드를 하위 클래스가 그대로 사용하거나, Override하여 사용하는 등의 구현을 할 수 있다.

 

이로 인해 다양히, 확장성있는 프로그래밍이 가능해진다.

https://www.youtube.com/watch?v=icQ22EckwWE

하지만 컴파일러 입장에선 상속이 가능한 이상 Override가 가능하고,  특정 메서드가 Override가 되는지 안되는지 또한 알 수가 없다.

 

그래서 컴파일 타임엔 이를 알 수 없고, 런타임에 메서드 Override에 따라 어떤 메서드를 실행해야될지 알 수 있게 되는데 이 과정에서 실행할 메서드를 찾아야되는 오버헤드(탐색 비용)가 발생하여 성능상의 문제를 일으킨다.

 

메서드를 찾는다는 것은 vTable이라는 정적(타입) 메모리 내부에 있는 테이블에 위치한 함수의 포인터(주소값)을 탐색하는 것이다.

 

이까지가 Dynamic Dispatch의 대표적인 특성이다.

 

그럼.. 어떻게 이러한 단점을 해결할 수 있을까? 여러 방법이 있겠지만, 우선 상속 관련한 방법을 보자

 

클래스를 사용하여도 상속을 지원하지 않는다면?, 난 그냥 인스턴스 하나만 공용으로 사용하고싶어서 클래스를 쓴거다 라면?

 

상속을 지원하지 않으면 된다.

 

상속을 지원하지 않으면, 클래스의 Dynamic Dispatch를 Static Dispatch로 동작하게 할 수 있다.

 

1. final 

상속을 지원하지 않을 클래스, 메서드, 프로퍼티에 final을 붙임으로써 Static Dispatch로 동작하게 할 수 있다.

 

2022.02.20 - [iOS/Swift] - Swift) final

 

Swift) final

final? 출처 Swift에선 상속을 지원한다!! final 키워드를 클래스에 앞단에 붙이면 해당 클래스는 더 이상 상속하지 않겠다.를 의미한다. 상속하지 않겠단걸 보고, 어.. 클래스만 상속 가능하니깐

limjs-dev.tistory.com

 

이전 글에서 알아본적이 있으니.. 이전 글을 보는게..

 

2. private

private을 활용하면 해당 키워드를 붙인 타입, 메서드, 프로퍼티에 접근할 수 있는 범위가 속된 파일까지로 제한된다.

 

이로 인해 다른 파일에선 private이 붙은 것들의 존재를 알지 못하기에 당연히 외부에선 상속, override가 불가능해진다.

 

그렇다면 컴파일러 입장에선 넓은 범주에서 상속, override 유무를 체크했어야 했는데, private이 붙은 것들은 같은 파일 내에서만 참조가 가능하므로 해당 파일 내부에서만 유무를 판단하여 내부적으로 final 키워드를 추론한다.

 

 

3. WMO (Whole Module Optimization) 

Swift는 기본적으로 컴파일시 모듈 내 파일들을 각각 컴파일한다.

 

이를 WMO라는 것을 활용하여 모듈 전체를 두고 컴파일하여 internal level의 클래스들이 오버라이딩 되는지를 추론하여 되지 않는 경우 내부적으로 final을 붙인다.

 

파일 하나하나 컴파일할 경우 각 파일만 검사하므로 다른 파일에서 해당 파일에 위치한 클래스가 오버라이딩 되는지를 파악할 수 없는 문제가 있어 dynamic dispatch로 동작하게 되는데 검사범위를 모듈 전체로 묶어버려 오버라이딩 되는지를 파악한 뒤 오버라이딩 되지 않으면 내부적으로 final을 붙여 static dispatch로 동작하게 하는 것이다.

 

하지만 이는 internal level일 때의 상황이고, 만일 open의 접근 레벨이라면 외부 모듈에서 참조, 상속이 가능하므로 WMO를 적용하여도 dynamic dispatch로 동작한다고 한다.

 

Public은 상속이 되지 않음에 대한 부가설명

 

'iOS > Swift' 카테고리의 다른 글

[Swift] mutating  (2) 2022.05.08
[Swift] Hashable  (0) 2022.05.02
Swift) COW(Copy On Write)  (0) 2022.02.20
Swift) self vs Self  (0) 2022.02.20
Swift) hitTest  (0) 2022.02.20

댓글