티스토리 뷰

안녕하세요 Hani입니다.

이번에는 WWDC16에서 발표된 Understanding Swift Performance에 대하여 알아볼 거예요. ☺️

 

 

해당 토픽이 다루는 목차는 총 5개로 이루어져 있어요.

1. Allocation

2. Reference Counting

3. Method Dispatch

4. Protocol Types

5. Generic Code

 

이번 포스팅에서는 Method Dispatch에 대하여 다뤄보려고 합니다.

 

컴파일타임에 실행할 메서드를 결정할 수 있을 때 이 것을 Static Dispatch로 동작을 한다고 말합니다.

컴파일타임에 결정된 메서드의 메모리 주소를 런타임에 바로 찾아가니 런타임의 효율이 증가한다고 볼 수 있어요. 👍

 

inlining에 관해서는 곧이어 설명드리겠습니다.

 

Dynamic Dispatch는 런타임에 실행할 메서드를 결정하는 방식입니다.

메서드 목록이 적혀있는 테이블을 런타임에 뒤져서 적절한 메서드를 찾는 방식이기 때문에 Static Dispatch보다 느립니다.

테이블에 관해서는 아래에서 다시 언급을 할게요. 😊

 

Static Dispatch에서는 가능했던 inlining을 비롯한 최적화가 Dynamic Dispatch에서는 안됩니다.🔥

 

inlining은 예시를 통해 알아봅시당.

구조체로 구현되어있는 Point입니다.

drawAPoint와 draw가 모두 static dispatch로 동작할 것이기 때문에

 

drawAPoint를 draw로 바꿀 수 있습니다. 심지어

 

draw메서드의 안쪽 부분을 가져다 놓은 것과 동일하므로 drawAPoint가 결국 저런 형태까지 변할 수 있어요. ☺️

 

 

이렇게 컴파일타임 때 실행될 부분을 정해놓으면

drawAPoint에서 draw로, draw에서 메서드 내부로 뛰어다니는 과정을 생략할 수 있기 때문에 성능이 좋아집니다.

즉, Inlining은 컴파일러가 메서드의 호출을 메서드의 바디 부분으로 교체하는 작업입니다.
메서드를 호출할 때 메서드 종료 이후 돌아올 곳을 기억하거나
스택에 전달인자를 푸쉬하는 등의 오버헤드가 줄어들기 때문에 
의미 있는 성능 향상을 줄 수 있습니다.

 

 

inlining을 통해 얻을 수 있는 이점을 동작으로 확인해 봅시다.

구조체를 위한 스택을 할당하고

 

인스턴스를 만든 다음에

 

drawPoint도 draw도 호출하지 않고

draw 메서드의 바디 부분을 곧바로 실행합니다. 🥰

 

 

원래대로라면 함수가 실행됐을 때 스택 포인터가 움직이는 등의 오버헤드가 필요하지만

inlining을 통해 그런 일을 할 필요가 없어집니다. 👍

 

오직 구조체를 인스턴스화하기 위한 스택 포인터의 이동이 필요할 뿐이에요. 🥰

 

 

 

 

아니 그럼 잘난 Static Dispatch만 쓰지 Dynamic Dispatch는 왜 필요한데 😡

 

이는 다형성을 활용하기 위함인데욥.

구조체가 아닌 클래스에 관한 예시를 살펴보겠습니다.

Drawable 클래스를 Point와 Line클래스가 상속받아 메서드를 오버라이드하고, 프로퍼티를 추가한 모습입니다.

공통 클래스인 Drawable을 담는 배열인 drawables를 순회하며 각 element의 draw메서드를 호출하고 있네요.

 

 

배열은 힙 영역의 인스턴스를 가리키는 주소를 저장하고 있기 때문에 각 element의 크기가 같습니다.

 

각 element에 적혀있는 주소로 인스턴스가 있는 힙 영역으로 접근할 수 있어용

 

 

 

element의 draw는 누구의 draw일까요?

Drawable, Line, Point 클래스 중 어느 메서드를 실행해야 할지 컴파일타임에는 알 수 없습니다.

 

컴파일러는 이를 알기 위해 클래스에 type이라는 필드를 추가하는데,

이 필드는 클래스의 타입 정보에 대한 포인터입니다.

 

인스턴스를 생성할 때 만들어진 파란색 메모리 부분은 Type 포인터와 Reference Count였던 것!

 

포인터를 따라서 Virtual Method Table (= vtable) 이라는 곳으로 이동합니다. (우상단 테이블)

 

이 테이블에는 안에는 실행해야 할 메서드들에 대한 포인터가 있어요.

이 포인터를 따라가면 정말 우리가 실행해야 할 메서드를 찾을 수 있습니다. ☺️

 

 

Method Dispatch에 대하여 클래스와 구조체를 비교해보면!

기본적으로 클래스는 힙 영역에 할당되고, 참조 타입이기 때문에 Reference Counting를 갖고 있습니다.

별 다른 키워드가 없다면 Dynamic Dispatch로 동작합니다.

 

final 키워드를 통해 상속을 막은 클래스라면 해당 클래스의 메서드는 Static Dispatch로 동작하게 됩니다. 🥰

 

구조체의 메서드는 전부 Static Dispatch로 동작하구용.

 

 

스위프트 성능에 관한 Method Dispatch에 대하여 알아봤습니다. 🥰


References

https://developer.apple.com/videos/play/wwdc2016/416/

 

 

댓글