Swift Digest
Blog | Swift.org Blog

Benchmark パッケージの登場: ユニットテストを性能チェックで補完する

Introducing the Benchmark Package: Complementing Unit Tests with Performance Checks

このダイジェストはClaude Opus 4.7 / 4.8によって生成されたものです(License)。原文はこちら

この記事の要点

何が発表されたのか

Benchmark は、Swift パッケージの性能を計測・検証するためのオープンソースパッケージです。「動くようにする・正しくする・速くする」という開発の指針のうち、最後の「速くする」を継続的に支えることを目的としています。

ユニットテストやインテグレーションテストが、機能が壊れたときにそれを検知してくれるのと同じように、ベンチマークを用意して継続的に実行すれば、性能が期待どおりでなくなったときにそれを検知できます。問題が見つかったら、Instruments・DTrace・Heaptrack・Leaks・Sample などの専用ツールで根本原因を分析して修正する、という流れになります。これは、ユニットテストの失敗をきっかけにデバッガや sanitizer で原因を突き止めるのと同じ構図です。

このパッケージは、マルチプラットフォーム対応・豊富なメトリクス・CI 連携・開発者にとっての扱いやすさという要件を満たす既存の解決策が Swift エコシステムに見当たらなかったため、専門的なトレーディングソフトウェアの開発で培われた知見をもとに開発され、オープンソース化されたものです。

何に使えるのか

Benchmark は SwiftPM のコマンドプラグインとして実装されており、次のコマンドでベンチマークを実行します。

swift package benchmark

ベンチマークは、計測したい処理を Benchmark のクロージャ内に書くだけで定義できます。たとえば Foundation.Date() の生成性能を計測する最小のベンチマークは次のようになります。

import Benchmark
import Foundation

let benchmarks = {
    Benchmark("Foundation-Date") { benchmark in
        for _ in benchmark.scaledIterations {
            blackHole(Foundation.Date())
        }
    }
}

benchmark.scaledIterations を使って計測対象の処理を繰り返し、blackHole(_:) に結果を渡すことで、コンパイラの最適化によって計測対象が消されてしまうのを防ぎます。CPU 使用量を中心に見るマイクロベンチマークから、長時間動く複雑なベンチマークまで対応でき、HDR Histogram パッケージを利用して、長時間にわたる多数のサンプルを計測できます。

計測できるメトリクス

Benchmark は豊富な組み込みメトリクスを備えています。代表的なものを挙げます。

このほか、キャッシュのヒット率などアプリケーション固有の値を計測するカスタムメトリクスもサポートされます。Linux・macOS それぞれの OS 固有メトリクスにも対応しており、サーバーサイド・デスクトップ・モバイルの各環境でリソース使用量を細かく把握できます。

メトリクスや実行回数のカスタマイズ

計測するメトリクスやスケーリングファクターは、Benchmark.Configuration で個別に指定できます。また、startMeasurement()stopMeasurement() を使えば、セットアップのコストを計測対象から除外できます。

import Benchmark
import Foundation
import Histogram

let benchmarks = {
    let customBenchmarkConfiguration: Benchmark.Configuration = .init(
        metrics: [
            .wallClock,
            .throughput,
            .syscalls,
            .threads,
            .peakMemoryResident
        ],
        scalingFactor: .kilo
    )

    Benchmark("ValueAtPercentile", configuration: customBenchmarkConfiguration) { benchmark in
        let maxValue: UInt64 = 1_000_000

        var histogram = Histogram<UInt64>(highestTrackableValue: maxValue,
                                          numberOfSignificantValueDigits: .three)

        for _ in 0 ..< 10_000 {
            blackHole(histogram.record(UInt64.random(in: 10 ... 1_000)))
        }

        let percentiles = [0.0, 25.0, 50.0, 75.0, 80.0, 90.0, 99.0, 100.0]

        benchmark.startMeasurement() // ここまでのセットアップは計測しない

        for i in benchmark.scaledIterations {
            blackHole(histogram.valueAtPercentile(percentiles[i % percentiles.count]))
        }

        benchmark.stopMeasurement()
    }
}

出力と分析

実行結果はデフォルトでは人間が読みやすい表形式で出力されます。これに加えて、他の可視化ツールでの分析に適したさまざまな出力フォーマットへのエクスポートにも対応しています。

サポートされる主なワークフロー

Benchmark は、次のような性能管理のワークフローを支援します。

導入・今後の位置づけ

ローカルでの手軽な性能検証から CI での自動チェックまでをカバーするため、個々の開発者は変更を push する前に素早く性能を確認でき、チームとしては性能のリグレッションを継続的に防げます。

発表時点で、Swift FoundationSwiftPMSwiftNIO・Google Flatbuffers といった主要なオープンソースプロジェクトが、性能最適化のために Benchmark パッケージを採用し始めていることが紹介されています。導入方法はドキュメントにまとめられており、Swift フォーラムの Benchmark カテゴリで議論できます。

関連リンク