검색결과 리스트
dynamic dispatch에 해당되는 글 1건
- 2015.07.16 [swift] 성능향상 시키기 - 다이나믹 디스패치를 최소화
글
[swift] 성능향상 시키기 - 다이나믹 디스패치를 최소화
애플 Swift 블로그의 [Increasing Performance by Reducing Dynamic Dispatch] 를 번역한 글입니다.
다른 언어들처럼 Swift도 수퍼클래스에 선언된 메서드와 속성을 오버라이드 할수 있습니다.
이말은 곧 프로그램이 런타임에 어떤 메서드와 프로퍼티를 참조할 것인지 결정해야하고, 간접호출 또는 간접접근을
수행해야 한다는 것을 의미합니다. 다이나믹 디스패치(dynamic dispatch)라고 불리는 이 기술은,
간접 호출/접근 횟수마다 일정량의 런타임 오버헤드 비용을 증가시킵니다. 성능에 민감한 코드에서는 그와같은 오버헤드
종종 바람직하지 않습니다. 이 포스트는 다이나믹함을 제거함으로써 성능을 향상시키는 3가지 방법에 대해 알려드립니다.
: final, private, 모듈 최적화.
다음 예를 살펴보겠습니다.
class ParticleModel {
var point = ( 0.0, 0.0 )
var velocity = 100.0
func updatePoint(newPoint: (Double, Double), newVelocity: Double) {
point = newPoint
velocity = newVelocity
}
func update(newP: (Double, Double), newV: Double) {
updatePoint(newP, newVelocity: newV)
}
}
var p = ParticleModel()
for i in stride(from: 0.0, through: 360, by: 1.0) {
p.update( (i * sin(i), i), newV: i*1000 )
}
작성된데로, 컴파일러는 동적으로 호출을 dispatch 할 것입니다.
1. p의 upodate 호출
2. p의 uodatePoint 호출
3. p의 point 튜플 속성에 접근한다
4. p의 velocity 속성에 접근한다
이것은 코드를 보면서 기대했던 것이 아닐수 있습니다. 동적 호출은 ParticleModel의 서브클래스들이 point 또는 velocity 를
계산속성이나 새롭게 구현된 updatePoint() 또는 update()로 오버라이딩 할수 있기 때문에 필수적입니다.
Swift에서 동적 디스패치콜은 메서드 테이블에서 함수를 찾고 간적호출을 수행하는 방식으로 구현되어집니다. 이것은 직접호출로
수행하는 것보다 더 느립니다. 게다가 간접호출은 많은 컴파일러 최적화를 막아 간접호출이 훨씬 더 많은 비용이 들도록 합니다.
성능 크리티컬한 코드에서 성능을 향샹시킬 필요가 있을때 다이나믹 행동을 제한하기 위해 사용할 수 있는 기술들이 있습니다.
1. 오버라이딩 할 필요가 없는 속성 선언에는 final을 사용하세요.
final은 클래스, 메서드, 속성이 오버라이드 할 수 없도록 제약하는 키워드입니다. 이렇게 하면 컴파일러가 다이나믹 디스패치의
간접호출/접근을 생략할 수 있게 합니다. 아래 예제의 point, velocity는 객체의 저장속성으로부터 직접적으로 액세스되고, updatePoint()는 direct 함수콜을 통해 호출됩니다. 반면 udpate()는 여전히 동적 디스패치를 통해 호출되며 서브클래스가 update()를 오버라이 할 수 있습니다.
class ParticleModel {
final var point = ( x:0.0, y:0.0 )
final var velocity = 100.0
final func updatePoint(newPoint: (Double, Double), newVelocity: Double) {
point = newPoint
velocity = newVelocity
}
func update(newP: (Double, Double), newV: Double) {
updatePoint(newP, newVelocity: newV)
}
}
var p = ParticleModel()
for i in stride(from: 0.0, through: 360, by: 1.0) {
p.update( (i * sin(i), i), newV: i*1000 )
}
final를 사용하여 전체 클래스 자체를 final로 만들수 있습니다. 이렇게 하면 서브클래싱이 불가능해지며 클래스의 모든 함수와 속성들이 final이 됩니다.
final class ParticleModel {
var point = ( x: 0.0, y: 0.0 )
var velocity = 100.0
// ...
}
2. private 키워드를 사용하면 한 파일에서만 참조되는 final 선언임을 추론할수 있습니다.
private 키워드를 선언부에 적용하면 현재파일에만 선언이 노출되도록 가시성을 제한합니다. 이것은 컴파일러가 모든 잠재적인 오버라이딩 선언을
찾을 수 있도록 합니다. 오버라이딩 선언이 없으면 컴파일러는 자동으로 final 키워드를 추론하고 메서드와 속성 접근에 대해 간접호출
(indirect calls)들을 제거합니다.
현재 파일에 ParticleModel 클래스에 대한 오버라이딩이 없다고 판단되면, 컴파일러는 private으로 선언된 모든 dynamic dispatch call을
직접호출로 대체합니다.
class ParticleModel {
private var point = ( x:0.0, y:0.0 )
private var velocity = 100.0
private func updatePoint(newPoint: (Double, Double), newVelocity: Double) {
point = newPoint
velocity = newVelocity
}
func update(newP: (Double, Double), newV: Double) {
updatePoint(newP, newVelocity: newV)
}
}
var p = ParticleModel()
for i in stride(from: 0.0, through: 360, by: 1.0) {
p.update( (i * sin(i), i), newV: i*1000 )
}
이전 예제에서처럼, point, velocity는 직접접근되며 updatePoint()는 직접호출 됩니다. 하지만 update()는 private이 아니기
때문에 간접적으로 호출됩니다.
final과 마찬가지로 클래스 선언부에 private을 사용하여 클래스 자체를 private로 만들고 그에 따라 모든 속성과 메서드를 private하게 만들
있습니다.
private class ParticleModel {
var point = ( x: 0.0, y: 0.0 )
var velocity = 100.0
}
3. internal 선언을 final로 추론할수 있는 전체 모듈 최적화를 사용하세요.
internal 접근자를 갖는 선언(디플트값)은 그들이 선언된 모듈 내에서만 볼수 접급될 수 있습니다. swift는 일반적으로 모듈을 구성하는
파일들을 개별적으로 컴파일하기 때문에 컴파일러는 internal 선언이 다른 파일에서 오버라이딩 됐는지 알수 없습니다. 그러나 전체 모듈최적
옵션을 활성화하면 모든 모듈이 동시에 한번에 컴파일 됩니다. 이렇게 하면 전체 모듈에 대해 컴파일러가 추론할수 있게되어 오버라이딩 되지
않은 internal 선언을 final로 추론할 수 있게됩니다.
원래 코드로 되돌아가서 이번에는 ParticleModel에 몇몇 public 키워드를 추가해보겠습니다.
public class ParticleModel {
var point = ( x:0.0, y:0.0 )
var velocity = 100.0
func updatePoint(newPoint: (Double, Double), newVelocity: Double) {
point = newPoint
velocity = newVelocity
}
public func update(newP: (Double, Double), newV: Double) {
updatePoint(newP, newVelocity: newV)
}
}
var p = ParticleModel()
for i in stride(from: 0.0, through: 360, by: 1.0) {
p.update( (i * sin(i), i), newV: i*1000 )
}
모듈 최적화(Module Optimization) 옵션을 활성화하고 이 코드를 컴파일하면, 컴파일러는 point, velocity,
updatePoint() 함수를 final로 추론할수 있습니다. 반면 udpate()는 public 접근자를 갖기 때문에 final로 추론
될 수 없습니다.
'Swift' 카테고리의 다른 글
| [swift] 문자열과 문자 -1 (Strings and Characters) (0) | 2015.08.01 |
|---|---|
| [swift2] Xcode7 beta4 Swift 언어 변경사항 (0) | 2015.07.30 |
| [swift] 성능향상 시키기 - 다이나믹 디스패치를 최소화 (0) | 2015.07.16 |
| [swift2] Xcode7 beta3 Swift 언어 변경사항 (0) | 2015.07.11 |
| [swift2] Swift에서 에러 처리 (Error Handling) (0) | 2015.07.10 |
| [swift2] 타입의 텍스트적인 표현 커스터마이징하기 (0) | 2015.07.01 |