Swift実行ファイルを逆アセンブルして、最適化具合を正確に把握する方法

Swiftは`Optimization Level`によって顕著にパフォーマンスに差が出ます。
参考: [Apples to apples, Part II · Jesse Squires](http://www.jessesquires.com/apples-to-apples-part-two/)

また、`Dynamic Dispatch`での呼び出しもオーバーヘッドになります。
参考: [Swiftのfinal・private・Whole Module Optimizationを理解しDynamic Dispatchを減らして、パフォーマンスを向上する - Qiita](http://qiita.com/mono0926/items/f5f271b7d2bde68207b2)

ドキュメントなど読み解けば「どういう記述をすればどうコンパイルされるか」は大体予測付きますが、やはり実際にその予測通りになっているかは確認しておきたい時があります。

特にロガーなどグローバルに呼び出されるものの場合、そういう確認大事だと思っています。
(この記事もロガーの検証が元々の目的で、そのために調べてまとめています。ロガーについても記事化予定です。)

※「逆コンパイル」ではなく「逆アセンブル」の方が良い表現だと思ったので、タイトル変更しました。

# [Instrumentsツール](https://developer.apple.com/library/mac/documentation/DeveloperTools/Conceptual/InstrumentsUserGuide/Introduction/Introduction.html)は？

パフォーマンス計測としては、[Instrumentsツール](https://developer.apple.com/library/mac/documentation/DeveloperTools/Conceptual/InstrumentsUserGuide/Introduction/Introduction.html)を使うことが多いと思います。

これは`Instrumentsツール`の`Time Profiler`の結果ですが、使いこなすとけっこうな情報が見られます。

![Screen Shot 2015-07-20 at 8.02.24 AM.png](https://qiita-image-store.s3.amazonaws.com/0/19398/94a24253-c9f7-fcf6-979a-d8f274a48593.png "Screen Shot 2015-07-20 at 8.02.24 AM.png")


どこのコードに時間かかっているのかの把握までは良いですが、機械語読み解くの大変ですし、`Instrumentsツール`使うまでもなくちょっとした検証コードがどうコンパイルされるか知りたいだけならよりシンプルな方法があります。

また後で紹介する[Hopper](http://www.hopperapp.com/)は、機械語を可読性の高いC言語っぽい疑似コードに整形してくれる機能があって、読み解きやすくなります。

# まずはコマンドラインでSwiftコードをコンパイルしてみる

適当なSwiftコードを用意します。

```sample.swift
func test() {
	print("hello")
}

test()
```

コンパイルします。(`-o`は出力ファイル名で、省略可能)


```sh
xcrun swiftc -Onone sample.swift -o result
```

`-Onone`は冒頭でも書いた`Optimization Level`の指定です。
Xcodeでビルドする場合、デフォルトでは以下のようになっています。

- Debug: `-Onone`
	- 最適化しない
- Release: `-O`
	- 最適化する
- `-Ounchecked`もある
	- 最適化する + 実行時のセーフチェックをしないので最も高速？
	- リリースビルドに非推奨([ios - Swift -Ounchecked and Assertions - Stack Overflow](http://stackoverflow.com/questions/30094582/swift-ounchecked-and-assertions))


実行するとこのようになります。

```sh
./result
# -> hello
```


ついでに、他のオプションとしては、`-D DEBUG`オプションなど付けると、以下の切り替えが可能です。

```swift
#if DEBUG
 someCall() // `-D DEBUG`オプション付けた時のみ有効
#endif
```

# `Optimization Level`の違いを観察

実行ファイルを逆アセンブルする方法は様々ありますが、[Hopper](http://www.hopperapp.com/)というアプリを使ってみます。

とりあえず、最適化あり無し版をそれぞれビルドしておきます。


```sh
# 最適化無し
xcrun swiftc -Onone sample.swift -o none
# 最適化あり
xcrun swiftc -O sample.swift -o optimized
# Ounchecked
xcrun swiftc -Ounchecked sample.swift -o unchecked
```

先日書いた[Swiftのfinal・private・Whole Module Optimizationを理解しDynamic Dispatchを減らして、パフォーマンスを向上する - Qiita](http://qiita.com/mono0926/items/f5f271b7d2bde68207b2)の検証のために、以下のコードを用意しました。

**最適化ありでも、多分`Dynamic Dispatch`になってしまうのでは？** と予想してましたが…、

```sample.swift
public class Mono {
    public func hello() {
        println("hello")
    }
}

public class Hoge: Mono {
    override public func hello() {
        println("hoge")
    }
}

let m = Mono()
m.hello()

let h: Mono = Hoge()
h.hello()
```

[Hopper](http://www.hopperapp.com/)というアプリを開いて、D&Dで実行ファイルを読み込むと逆コンパイルしてくれます。

![Screen_Shot_2015-07-20_at_9_29_20_AM.jpg](https://qiita-image-store.s3.amazonaws.com/0/19398/ce4c42bf-d03d-ff48-7ea1-ef98a7680dfd.jpeg "Screen_Shot_2015-07-20_at_9_29_20_AM.jpg")



`Show Pseudo Code`の機能を使うと、C言語ぽい疑似コードが見られます。

というわけで、先ほどのコードの逆アセンブル結果を見てみます。


## `-O`(最適化あり)の結果

予想に反して、直接呼び出しになっているように見えます…。
元々、上のSwiftコードはもっとシンプルな作りにしてあったのを、`Dynamic Dispatch`発生されるように少し冗長にしたのですが。
同じファイルに記述しちゃったせいですかね（´・ω・｀）
原因分かった方は教えてください（´・ω・｀）

```c
// _main
int _main(int arg0, int arg1, int arg2) {
    swift_once(_globalinit_33_1BDF70FFC18749BAB495A73B459ED2F0_token4, _globalinit_33_1BDF70FFC18749BAB495A73B459ED2F0_func4, 0x0);
    *(int32_t *)__TZvOSs7Process5_argcVSs5Int32 = arg0;
    swift_once(_globalinit_33_1BDF70FFC18749BAB495A73B459ED2F0_token5, _globalinit_33_1BDF70FFC18749BAB495A73B459ED2F0_func5, 0x0);
    *__TZvOSs7Process11_unsafeArgvGVSs20UnsafeMutablePointerGS0_VSs4Int8__ = arg1;
    _TFSs7printlnU__FQ_T_("hello", __TMdSS + 0x8);
    _TFSs7printlnU__FQ_T_("hoge", __TMdSS + 0x8);
    return 0x0;
}
```

`-Ounchecked`の時もほぼ同じ結果になりました。

## `-Onone`(最適化なし)の結果

こっちはかなりの量の実行コードが生成されていて、追うのが大変でした（´・ω・｀）

生成される関数名の命名規則は、「`モジュール名` + `関数名` + それらの名前の文字数 + その他」という構成になっているようです。「それらの名前の文字数」は今回は4ばかりですね。
参考: [mikeash.com: Friday Q&A 2014-08-15: Swift Name Mangling](https://mikeash.com/pyblog/friday-qa-2014-08-15-swift-name-mangling.html)


```c
// _main
int _main(int arg0, int arg1, int arg2) {
    swift_once(_globalinit_33_1BDF70FFC18749BAB495A73B459ED2F0_token4, _globalinit_33_1BDF70FFC18749BAB495A73B459ED2F0_func4, 0x0, _globalinit_33_1BDF70FFC18749BAB495A73B459ED2F0_func4);
    *(int32_t *)__TZvOSs7Process5_argcVSs5Int32 = arg0;
    swift_once(_globalinit_33_1BDF70FFC18749BAB495A73B459ED2F0_token5, _globalinit_33_1BDF70FFC18749BAB495A73B459ED2F0_func5, 0x0, __TZvOSs7Process5_argcVSs5Int32, arg0);
    *__TZvOSs7Process11_unsafeArgvGVSs20UnsafeMutablePointerGS0_VSs4Int8__ = arg1;
    __TF4none4testFT_T_();
    return 0x0;
}

// __TF4none4testFT_T_
int __TF4none4testFT_T_() {
    rax = __TMaC4none4Mono();
    rax = __TFC4none4MonoCfMS0_FT_S0_(rax);
    var_8 = rax;
    swift_retain_noresult(rax);
    (*(*var_8 + 0x48))(var_8);
    rax = __TMaC4none4Hoge();
    rax = __TFC4none4HogeCfMS0_FT_S0_(rax);
    var_18 = rax;
    swift_retain_noresult(rax);
    (*(*var_18 + 0x48))(var_18);
    swift_release(var_18);
    rax = swift_release(var_8);
    return rax;
}

// __TMaC4none4Mono
int __TMaC4none4Mono() {
    rax = *__TMLC4none4Mono;
    var_8 = rax;
    if (rax == 0x0) {
            rax = swift_getInitializedObjCClass(objc_class__TtC4none4Mono);
            *__TMLC4none4Mono = rax;
            var_8 = rax;
    }
    rax = var_8;
    return rax;
}

// 他にも色々...

// __TFC4none4Mono5hellofS0_FT_T_
int __TFC4none4Mono5hellofS0_FT_T_(int arg0) {
    rax = _TFSSCfMSSFT21_builtinStringLiteralBp8byteSizeBw7isASCIIBi1__SS("hello", 0x5, 0x1, 0x5);
    rdi = var_18;
    _TFSs7printlnU__FQ_T_(rdi, __TMdSS + 0x8); // やっとprint
    rax = swift_release(arg0);
    return rax;
}
```

# 終わり

というわけで、[Swiftのfinal・private・Whole Module Optimizationを理解しDynamic Dispatchを減らして、パフォーマンスを向上する - Qiita](http://qiita.com/mono0926/items/f5f271b7d2bde68207b2)の検証自体は思ったようにうまくいかなかったですが、どうコンパイルされるかが探れるようになって良かったです。

今後も気になった時に確認してみたりしようと思います。


# 参考資料

- [Secret of Swift Performance — Swift Programming — Medium](https://medium.com/swift-programming/secret-of-swift-performance-fcc5d2a437a8)
	- こちらを大いに参考にいたしました。