web-dev-qa-db-ja.com

Swift非常に短い関数で型チェックに時間がかかりすぎる

Swiftコンパイラフラグ_-warn-long-function-bodies_を90ミリ秒に設定しました。これは、プロジェクトのどの関数がコンパイルに時間がかかりすぎているか(型チェックのため))を確認するためです。

私は次の方法があります:

_func someKey(_ sectionType: SectionType, row: Int) -> String {
    let suffix = row == 0 ? "top" : "content"
    return "\(sectionType)_\(suffix)"
}
_

SectionTypeは文字列で裏付けられた列挙型です)

上記のように、2017 MacBook Proでは96msかかります。私が最初に試みたのは、文字列補間を回避し、\(sectionType.rawValue)の代わりに\(sectionType)を使用することですが、今では106 msが得られます。間違った動き...

次に、私は変更しました:

_let suffix = row == 0 ? "top" : "content"
_

に:

_let suffix = "top"
_

警告は消えるので、問題を引き起こしているのは三項演算子です。

代わりにこれを試しました:

_let suffix: String = { // Note the type annotation!
    if row == 0 {
        return "top"
    }
    return "content"
}()
_

...しかし今では97ミリ秒かかるclosureです(関数全体、101)。

私はより明示的なものを試しました:

_    let suffix: String = {
        if row == 0 {
            return String("top")
        } else {
            return String("content")
        }
    }()
_

...そして私は閉鎖を取得します:94ms;機能:98ms。

どうなっているの?

90ミリ秒の制限は短すぎますか? dictionaryリテラルの型チェックバグがあったことは知っていますが(これは?)、これはまったく別のように見えます...?

私の環境はXcode 8.3.2(8E2002)、Swift:Apple Swift version 3.1 (swiftlang-802.0.53 clang-802.0.42)です


しかし、待ってください!さらにあります...

私はこの関数本体を試しました:

_func someKey(_ sectionType: SectionType, row: Int) -> String {
    if row == 0 {
        return "\(sectionType.rawValue)_top"
    } else {
        return "\(sectionType.rawValue)_content"
    }
}
_

...そして97ms〜112msかかります!?


補遺:関数と列挙型をクリーンで最小限のプロジェクト(シングルビューアプリケーション)に移植しましたが、同じ警告を設定しましたが、発生しません。プロジェクト全体が何らかの形でこの1つの方法に影響を与えていると確信していますが、まだどの程度かはわかりません...


補遺2:関数の静的バージョンをテストしました:rowの値に関係なく、固定サフィックス「top」を使用します(これには90ミリ秒未満かかり、警告はトリガーされません)、- but次のifブロックを追加しました:

_func someKey(_ sectionType: SectionType, row: Int) -> String {
    if row == 0 {
        print("zero")
    } else {
        print("non-zero")
    }

    let suffix: String = "top"
    return "\(sectionType)_\(suffix)"
}
_

これにより、96〜98ミリ秒に戻ります。 行をゼロと比較すると問題が発生しますか?


回避策:私はコードをいじり続けましたが、ifブロックをswitchステートメントに置き換えると問題が解決することがわかりました。

_func someKey(_ sectionType: SectionType, row: Int) -> String {
    let suffix: String = {
        switch row {
        case 0:
            return "top"
        default:
            return "content"
        }
    }()
    return "\(sectionType)_\(suffix)"
}
_

(私はこれを本当に起こっていることの説明とは考えていないので、自分の質問には答えません)

19
Nicolas Miari

三項演算子だと思います。

Xcode 11(〜93ms)でも同様の結果が得られましたが、コンパイル時間は〜23msに短縮されます。

func someKey(_ sectionType: SectionType, row: Int) -> String {

    var suffix = "top"

    if row != 0 {
        suffix = "content"
    }

    return "\(sectionType)_\(suffix)"
}

この行のロジックを変更することで、メソッドが約1ミリ秒に短縮されるため、それが3値ロジックであることを証明できると思います。行をブール値にしました。

func someKey(_ sectionType: SectionType, row: Bool) -> String {
    let suffix = row ? "top" : "content"
    return "\(sectionType)_\(suffix)"
}

同様に(しゃれは意図されていませんが)3値論理をlet suffix = row != 0 ? "top" : "content"に変更すると、コンパイル時間が半分になります。これは私の最初のコードブロックに匹敵します。 Swiftの方が!=よりも理解しやすいのは==です。

2
Scott McKenzie