Swiftのfinal・private・Whole Module Optimizationを理解しDynamic Dispatchを減らして、パフォーマンスを向上する

# 追記(2016/10/23)

[SE-0117: Allow distinguishing between public access and public overridability](https://github.com/apple/swift-evolution/blob/master/proposals/0117-non-public-subclassable-by-default.md)で`public`の意味が少し変わって、`open`も登場した関係で、本記事中のコードはSwift 3では少し変更必要そうです。

- `final`がデフォルトになったので指定不要になった
- オーバーライド可能にするには`open`を明示が必要になった(これまではデフォルト`open`状態だった)

デフォルトでDynamic Dispatchが発生しにくくなった感じです。

表面上の書き方は少し変わりましたが、本質は変わりません。
少し違う観点ですが、新しい公式記事もあがっています:
[Swift.org - Whole-Module Optimization in Swift 3](https://swift.org/blog/whole-module-optimizations/)

---

Swiftパフォーマンス周りの話題だと、Swift Optimization Levelの違いでかなり差が出るのはわりと有名かと思います:
[Apples to apples, Part II · Jesse Squires](http://www.jessesquires.com/apples-to-apples-part-two/)


一方、`Dynamic Dispatch`について、[少し前(2015/4/9)にSwift公式ブログに載っていましたが](https://developer.apple.com/swift/blog/?id=27)、あまり話題になってないように思います。
特に個人的に`Whole Module Optimization`について理解したい事情もあり、読み解いてみました。


---

まず、親クラスのメソッドやプロパティのオーバーライドを実現するために、`dynamic dispatch`の仕組みが必要とのことです。これが実行時のオーバーヘッドとなります。

対象コード:

```swift
class ParticleModel {
	var point = ( 0.0, 0.0 ) // C
	var velocity = 100.0 // D

	func updatePoint(newPoint: (Double, Double), newVelocity: Double) { // B
		point = newPoint
		velocity = newVelocity
	}

	func update(newP: (Double, Double), newV: Double) { // A
		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)
}
```

## Dynamic Dispath発生箇所

- A: Call update on p.
- B: Call updatePoint on p.
- C: Get the property point tuple of p.
- D: Get the property velocity of p.


## どうしてDynamic Dispatchが発生するか

`ParticleModel`の子クラスが`point`・`velocity`・`updatePoint()`・`update()`をオーバーライドしうるため、動的にどのクラスのものを呼べば良いか判断する必要が出てきます。

## Dynamic Dispatchの処理のされ方

- メソッドテーブルから検索
- 間接的に呼び出し
	- 直接呼び出しより遅い
	- このために恩恵を受けられないコンパイル最適化が存在

# Dynamic Dispatchの減らし方

## オーバーライドされる必要が無いときは、`final`キーワードを使う

- `final`は、クラス・メソッド・プロパティに指定できる
- クラスに対して指定した時は、継承されないことをコンパイラーに明示する
- メソッド・プロパティに対して指定した時は、オーバーライドされないことをコンパイラーに明示する


以下に変更すると、

- point・verlocityプロパティ、updatePointメソッド: 直接呼び出しになる
- update: 間接的な呼び出し(dynamic dispatch)のまま

```swift
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)
	}
}
```

クラスに指定すると、継承自体が不可となります。
この時点で、プロパティ・メソッドはオーバーライド出来ないので、それらにはfinal付ける必要も無く直接呼び出しになります。

```swift
final class ParticleModel {
	var point = ( x: 0.0, y: 0.0 )
	var velocity = 100.0
	...
```

## `private`キーワードで`final`による直接呼び出し化と同等のことも可能

Swiftの`private`はファイルスコープなので、同一ファイルに定義すれば、こんなことが出来ちゃいます。

```swift
class Parent {
    private var value: String! { return "parent" }
}
class Child: Parent {
    private override var value: String! { return "child" }
}
```

この`Parent`クラスと`Child`が別ファイルに定義されていたらコンパイルエラーになります。

つまり、下記のように定義して、`ParticleModel`クラスを継承するクラスが同じファイルに無ければ、`private`で定義したプロパティ・メソッドは`final`でもあるとみなされます。

```swift
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)
	}
}
```

同様に、クラスを`private`にすれば、`ParticleModel`クラスを継承するクラスが同じファイルに無ければ、継承自体不可能になるため、プロパティ・メソッドはオーバーライド出来ず、それらにはfinal付ける必要も無く直接呼び出しになります。

```swift
private class ParticleModel {
	var point = ( x: 0.0, y: 0.0 )
	var velocity = 100.0
	// ...
}
```

## `Whole Module Optimization`をYesにする

Swiftのデフォルトアクセスレベルである`internal`は、モジュール内アクセスが可能です。
Swiftは通常モジュールを構成しているファイルを別々にコンパイルするので、`internal`なものがオーバーライドされるかが分からずdynamic dispatchされる状態になってしまいます。

しかし、`Whole Module Optimization`を`Yes`にすると、モジュールは同時にコンパイルされ、コンパイラーは`internal`なものがオーバーライドされるかどうか(モジュール内でオーバーライドされているかどうか)が分かるようになります。

例えば、下記のようにしたとしましょう。

`public`指定のため、クラスはモジュール外アクセスが可能です。
このとき、point・velocity・updatePointがモジュール内でオーバーライドされていなければ、オーバーライドされる可能性は無くなり、`final`とみなされ、直接呼び出しになります。

```swift
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: times, by: 1.0) {
	p.update((i * sin(i), i), newV:i*1000)
}
```

モジュールの話なので、Embedded Framework使った場合に限った話と思いきや、メインアプリもモジュールでもあるので、多分それについても同様かなと思っています。

### Xcode 6.3・Swift 1.2以降のインクリメンタルビルドとの関係

Xcode 6.3・Swift 1.2でインクリメンタルビルドが導入されて、ソースを少し書き換えた後のビルド速度がかなり上がりました。
しかし、`Whole Module Optimization`をYesにすると、インクリメンタルビルドが行われなくなりビルドが遅くなります。

Debugビルド: NO、Releaseビルド: YESなどが良いですかね？
Swift Optimization Levelの問題と同様、リリースビルドすると挙動がおかしくなったりしそうですが（´・ω・｀）
ただ、僕はこれに関してはまだ実行時の挙動の差異を実感したこと無いです。


参考: [Swift 1.2 Update (Xcode 6.3 beta 2) - Performance - Human Friendly](http://blog.human-friendly.com/swift-1-dot-2-update-xcode-6-dot-3-beta-2-performance)

### Xcode 6.3.1でのエンバグに注意

また、`Whole Module Optimization`は、Xcode 6.3.1ではエンバグのため、`Yes`にしか指定できませんので注意です。
参考: [Swift - Xcode 6系のバージョン間の微妙な差異まとめ - Qiita](http://qiita.com/mono0926/items/2ea3f6a9258ffb705b5f)

# `final`指定の本来の目的はオーバーライド不可を明示すること

`final`指定することでコンパイルレベルでオーバーライド不可になりますし、設計意図も示せます。
これが本来の目的で、副次的にパフォーマンス向上にも繋がる、という程度に捉えた方が良いと思っています。

# どのくらいパフォーマンスに差が出るのか

[The Importance of Being `final` - Human Friendly](http://blog.human-friendly.com/the-importance-of-being-final)の"How much difference does it make?"に記載がありました。

まず、Swift Optimization Levelが`None`だと差がほぼ出ないようです。まあインライン化とかしないですし納得です。
Swiftコード最適化をより効きやすくするもの、と捉えると良いですかね。

`Fastest`では、記事に書いてある比較では **17.04倍** も速くなったようです。


# 参考資料

- [Increasing Performance by Reducing Dynamic Dispatch - Swift Blog - Apple Developer](https://developer.apple.com/swift/blog/?id=27)
- [Swift 1.2 Update (Xcode 6.3 beta 2) - Performance - Human Friendly](http://blog.human-friendly.com/swift-1-dot-2-update-xcode-6-dot-3-beta-2-performance)
- [The Importance of Being `final` - Human Friendly](http://blog.human-friendly.com/the-importance-of-being-final)
- [Swift はどれくらい Swift(速い) ですか? - Realm is a mobile database: a replacement for SQLite & Core Data](https://realm.io/jp/news/swift-summit-joseph-lord-performance/)