Help us understand the problem. What is going on with this article?

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

More than 3 years have passed since last update.

SwiftはOptimization Levelによって顕著にパフォーマンスに差が出ます。
参考: Apples to apples, Part II · Jesse Squires

また、Dynamic Dispatchでの呼び出しもオーバーヘッドになります。
参考: Swiftのfinal・private・Whole Module Optimizationを理解しDynamic Dispatchを減らして、パフォーマンスを向上する - Qiita

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

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

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

Instrumentsツールは?

パフォーマンス計測としては、Instrumentsツールを使うことが多いと思います。

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

Screen Shot 2015-07-20 at 8.02.24 AM.png

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

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

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

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

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

test()

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

xcrun swiftc -Onone sample.swift -o result

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

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

./result
# -> hello

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

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

Optimization Levelの違いを観察

実行ファイルを逆アセンブルする方法は様々ありますが、Hopperというアプリを使ってみます。

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

# 最適化無し
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の検証のために、以下のコードを用意しました。

最適化ありでも、多分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というアプリを開いて、D&Dで実行ファイルを読み込むと逆コンパイルしてくれます。

Screen_Shot_2015-07-20_at_9_29_20_AM.jpg

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

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

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

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

// _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

// _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の検証自体は思ったようにうまくいかなかったですが、どうコンパイルされるかが探れるようになって良かったです。

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

参考資料

mono0926
プロフィール: https://stackoverflow.com/story/mono0926 以前開発していたJOIN US: http://joinus30.com 2015年に開発しててベストアプリにも選ばれたPlayer!: http://www.playerapp.tokyo
https://medium.com/@mono0926
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした