이번에는 Understand Swift Performance의 마지막!
Generic을 활용하여 성능을 향상시키는 방법에 대해 알아보자
지금까지 Swift의 성능에 대해 알아본 주요 내용은 다음과 같다.
다형성은 하나의 인터페이스나 메서드가 여러 형태로 동작하는 것을 의미한다. 이는 overriding과 overloadin을 통해 구현된다.(구조체의 경우 Protocol을 통해 다형성 지원)
overriding: 상속 관계에 있는 클래스 간에 메서드를 재정의 한다.
overloading: 같은 이름의 매서드를 매개변수의 타입, 개수, 순서 등을 다르게 하여 여러 번 정의한다.
제네릭을 사용하면 함수가 다양한 종류의 타입 매개변수에 대해 동일한 작업을 수행할 수 있다. 이는 매개변수화된 다형성을 의미하며 코드의 재사용성과 타입 안전성을 높인다.
정적 다형성은 컴파일 시점에 타입이 결정되는 다형성을 말한다. 제네릭을 사용하면 컴파일러는 제네릭 타입 매개변수를 실제 타입으로 대체하여 최적화된 코드를 생성한다. 이는 런타임에 성능을 향상시킨다.
protocol Drawable {
func draw()
}
// 1. 제네릭 사용 x
func drawACopy(local: Drawable) {
local.draw()
}
// 2. 제네릭 사용 o
func drawACopy<T: Drawable>(local: T) {
local.draw()
}
let line = line()
drawACopy(line)
let point = Point()
drawACopy(point)
local의 실제 타입을 컴파일 시점에 알 수 없기 때문에 런타임 중에 해당 타입의 draw() 메서드를 찾는 과정이 필요하다. 이는 동적 디스패치 방식으로 작동한다.
컴파일 시점에 매개변수 T를 실제 타입으로 대체한 코드를 생성한다. 이는 해당 타입의 draw()메서드를 정적 디스패치 방식으로 호출하게 하여 성능을 최적화 한다.
제네릭과 프로토콜을 함께 사용하면 성능을 개선할 수 있지만 부작용도 있다.
Understand Swift Performance의 마지막 글이다. 지금까지 개발자로서 Swift의 성능을 개선하는 방법을 배울 수 있었다. 유연성이 높으면 성능은 낮아지고 성능을 높이면 유연성이 낮아진다. 그렇기 때문에 항상 성능과 유연성 사이에 균형을 찾고 상황에 맞는 최적의 솔루션을 위해 항상 고민해야 한다.
이렇게 개념만 알고 넘어가지 말고 꼭 실제 프로젝트에 적용하도록 하겠다!