📝

Swift: print()は #if DEBUGで囲むべきか

print()はパフォーマンスに影響するのか?

Swiftにおいて何かデバッグ用の情報を出力するためにprint statementを使うことはとても頻繁にありますが、

print("Hello World!")

と書く人と

#if DEBUG
print("Hello World!")
#endif

と書く人がいます。

printは開発中にのみ必要なものなので、リリースされたアプリのビルドの中では不要な処理です。なので#if DEBUGで囲むことによってリリース用の処理から外すのは合理的と言えるでしょう。もしprint処理がパフォーマンスに少しでも影響するなら、リリース用のアプリから外すべきです。

しかし、printはパフォーマンスに影響するのか?
調べてみましたが公式ドキュメントはprint statementがアプリのパフォーマンスに影響するかどうかの記述は見つからず。。。

https://developer.apple.com/documentation/swift/print(_:separator:terminator:)

海外の記事を軽く漁ってみてもprintがパフォーマンスに影響する・しない どちらのエビデンスも見つからなかったので、実際に検証してみました。

検証

以下のようなScrollViewの中にLazyVStackがあり、その中身のrowが表示されるたびに10万回のprint処理を行うViewを作ってみました。これでスクロールするたびにとんでもない量のprint処理がよばれます。

このスクロールがスムーズにできるかどうかでパフォーマンスを判定します。

struct ContentView: View {
    var body: some View {
        ScrollView {
            LazyVStack {
                ForEach(0..<1000, id: \.self) { index in
                    Text("Row \(index)")
                        .padding()
                        .onAppear {
                            for i in 0...100000 {
                                print("Row \(index) appeared \(i)")
                            }
                        }
                }
            }
        }
    }
}

1. printあり・Debug Mode (Xcodeと繋がった状態)

まずは大量のprintが実装されているビルドで動作を見てみましょう。
このように 全く動きません。 10万回のprintを一つのrowが出現するたびに行われるので当然ですね。わかりにくいですがマウスで激しくスクロールしています。しかし動きません。

2. printなし・Debug Mode

次はさっきと同じコードだがprint処理はコメントアウトされたものを試します。

struct ContentView: View {
    var body: some View {
        ScrollView {
            LazyVStack {
                ForEach(0..<1000, id: \.self) { index in
                    Text("Row \(index)")
                        .padding()
                        .onAppear {
                            for i in 0...100000 {
                                // print("Row \(index) appeared \(i)")
                            }
                        }
                }
            }
        }
    }
}

このようにサクサク動いています。

3. printあり・Normal Mode (Xcodeと繋がっていない状態)

printありの状態かつXcodeと繋がってない状態で動作確認すると、 スクロールできるけど若干もたつく という結果になりました。Gifなのでわかりにくい。

注目するべきなのは、print statementはxcodeと繋がっていないときの負荷はかなり小さい ということです。10万回のprintが1列ごとによばれているのにも関わらずスクロールできています。

4. printなし・Normal Mode

printなしの状態かつXcodeと繋がってない状態で動作確認すると、 ヌルヌルでした。

5. printを#If DEBUGで囲い込む・Normal Mode

このように if DEBUGで printを囲ってConfigurationをReleaseにして動作を確認します。

struct ContentView: View {
    var body: some View {
        ScrollView {
            LazyVStack {
                ForEach(0..<1000, id: \.self) { index in
                    Text("Row \(index)")
                        .padding()
                        .onAppear {
                            for i in 0...100000 {
                                #if DEBUG
                                print("Row \(index) appeared \(i)")
                                #endif
                            }
                        }
                }
            }
        }
    }
}

結果は4と同じ ヌルヌルでした。

まとめ

printは、
1. Debug Modeだとパフォーマンスに大きな負荷がかかる
2. Normal Modeだとパフォーマンスに負荷がかかるが, Debug Modeでの負荷と比較するとかなり小さい
3. #if DEBUGで囲うことで負荷を取り除くことができる

なので、結論 printは #if DEBUGで囲んだ方が良い というのが私の見解です。

Gifだとぬるぬる具合わかりにくくてすみません。気になる方は上記のコードをコピペするだけで簡単に手元で試せるので確認してみてください!

Voicyテックブログ

Discussion

YJunYJun

疑問に思ったので質問なのですが、 printがパフォーマンスに影響する かを調査するために、SwiftUIのScrollViewを利用して調査を行っているのは何故なのでしょうか。影響するファクターが多いため printがパフォーマンスに影響する ことの調査としては合理性を欠くように思われます。

以下のうち2については証明できているとする根拠がないのではないでしょうか。

  1. Debug Modeだとパフォーマンスに大きな負荷がかかる
  2. Normal Modeだとパフォーマンスにほんの少し負荷がかかるが, Debug Modeの1%以下程度
  3. #if DEBUGで囲うことで負荷を完全に取り除くことができる
YJunYJun

ありがとうございます。ただ、依然として測定方法の合理性については不適切であり、何のエビデンスにもなっていないように感じますので、全体的な記載方法を再考いただくことをお勧めします。

ムッチョ | iOSエンジニアムッチョ | iOSエンジニア

ありがとうございます。今回使用したコードは対照実験的にprint statement以外は同じ条件で振る舞いを比べていて、printがある状態がDebugModeでなくてもprintがない状態に比べて明らかにパフォーマンスが落ちることを確認できているのでprintがパフォーマンスに影響すると結論づけるための十分なエビデンスになるかと! 10万回繰り返されるfor in statementなどもprint無い方にもちゃんと残しておきました