Skip to content

Swift Objective Cpp Performance Audit 2026 10 22

log5r edited this page Oct 22, 2026 · 1 revision

Objective-C++ / C++ への変更と性能の追加調査(2026-10-22)

Objective-C++ / C++ への移植を比較する価値があるのは、音声コールバックと数値・バイト列の一括処理である。ただし、今回の調査では、製品コードを移植する根拠となる速度優位は確認できなかった。動画EQの計算部分では、現行Swift相当とC++の限定比較を実行した。バッファ再利用や所有権の変更で改善を見込める部分もあるが、Swiftで同じ変更を行う場合との比較が必要になる。

対象はコミット 269937a56e0045681806f3da5ce6fd5f857012ec。前回の全Swiftファイル調査を踏まえ、音声処理、解析、バイナリ形式の編集、検索・描画の候補を処理単位で再評価した。このページでは候補、比較結果、採用条件を記録する。製品ソースの変更は行っていない。

Objective-C++ と C++ を使う範囲

このプロジェクトは、既に PitchShiftAudioUnit.mm で Objective-C++ を使用している。AUAudioUnitとの接続をObjective-Cで実装し、render blockはraw pointerでC++の状態を参照して音声を処理する。配列のサイズ変更や確保は初期化側へ置き、render側が使うatomic型のlock-free性をビルド時に検査している。新しい音声処理をC++化する場合は、この分割を参考にできる。

Objective-C++の .mm へ変更するだけで、確保・コピー・ロック待ちがなくなるわけではない。改善案は、AppleのオブジェクトAPIを扱う窓口を薄くし、連続したサンプル列と処理状態をC++内でまとめて扱うことになる。1サンプルごとの言語間呼び出しや、PCMのオブジェクト配列化は避ける。

動画EQの限定比較

対象は VideoEqualizerProcessor.processChannel の演算部分。現行の式・係数と状態の型・bandFrequencies と bandCount の定義を保って独立関数へ取り出し、同じ演算順のC++と比較した。追加でSwiftのループ回数を定数10にした版と、C++のループ回数を引数で受け取る対照群も測定した。

Apple M3、macOS 27.0.1、Apple Swift 6.4、Apple clang 21を使用した。Swiftは -O -whole-module-optimization、C++は -O3 -std=gnu++20 -ffp-contract=off -fno-fast-math。両者ともmacOS26.5をtargetにし、数値関数のinliningを禁止した。係数は48 kHz用の非ゼロの10バンド設定で、共通のFloat32入力を使用した。

64 / 128 / 512 / 1024 / 4096フレーム、stereoのplanar / interleavedという10条件で、warmup後に各実装を8試行した。各試行は多数のbufferをまとめて実行し、所要時間をbuffer数で割った。下表は512フレームについて、その8値の中央値である。全実装で共通の入力コピーと外側の反復を含み、callbackを1回ずつ測った中央値やp99ではない。

512フレーム・2チャンネル planar(µs/buffer) interleaved(µs/buffer)
現行Swift相当 9.586 9.515
Swift:ループ回数を定数10で指定 9.649 9.489
C++:ループ回数を定数10で指定 9.653 9.578
C++:ループ回数を引数で指定(10を渡す対照群) 24.510 23.881

全10条件の「現行Swift時間 ÷ C++固定10時間」は 0.993〜1.029 だった。設定によって差の方向も変わり、この実験ではC++固定10による一貫した改善を確認できなかった。全条件で16bufferを連続処理した出力とフィルター状態を比較し、各実装379,136個のFloat値がbit単位で一致した。timing後のchecksumも一致している。

生成コードでは、現行Swift相当の bandCount は10へ畳まれ、演算が展開されていた。Swift固定10と現行相当はラベルを除く159命令が同じで、抽出した数値関数内にはARC・確保・排他アクセス検査のruntime呼び出しがなかった。C++引数版が固定10より約2.4〜2.6倍遅かったのは、定数情報と最適化結果の違いを示す対照例である。C++全般の性能差としては扱わない。

この結果は、1台のMac上の抽出コードについての測定に限られる。実アプリのinlining、EQの外側にある配列借用、atomic・設定の寿命管理、Objective-C++の窓口、AVPlayer callback、実際の音切れや電力、iPhone/iPadは測定していない。係数と入力も一組であり、全入力の正しさを検証したものではない。C++側はC ABIで呼び出している。先行したC++17の実行とgnu++20の再計測でも主な結論は変わらなかった。

実験ソース、再現手順、全試行の生データと生成コードは、ローカルの tmp/cpp-performance-audit-20261022/video-eq-kernel/ に保存した。製品バイナリーを変更した性能測定とは区別する。

比較候補と優先順位

優先度 範囲 差が出る可能性と現時点の判断
1:実callbackの計測 VideoPlayerService.swift + VideoEqualizerProcessor.swift + VideoAudioSampleRing.swift C++の状態と事前確保領域にまとめ、callback中の参照管理・排他アクセス検査・配列借用の費用を減らせる可能性がある。ただし、現行SwiftでもEQ内側はpointer、ringは事前確保とatomicを使用している。今回の計算部分の比較ではC++の優位を確認できず、callback全体の生成コードと処理時間のばらつきが次の判断材料になる。
2:Swift改善を対照に比較 SpectrumAnalyzer.swift / SpectrumFFT.swift / VideoSpectrumAccumulator.swift PCM配列の生成、FFTの作業配列、履歴のコピーと先頭削除を、持続する作業領域へ変更する余地がある。C++ coreでも実装できるが、FFT本体は既にAccelerateのvDSP。まずSwiftで同じバッファ再利用を行い、その残りの費用を比較する。
3:操作条件を絞って比較 MP4MetadataWriter+Boxes.swift stco/co64の多数のentryをData添字で読み書きする部分は、小さなC++ buffer処理と比較できる。実行条件は、全書き換えでmoovのサイズが変わり、後続データのoffset補正が必要になる場合。通常のin-place編集を含む全操作が速くなるとは判断しない。Swiftで一度だけmutable bytesを借用する案を対照にする。
3:大きなファイルの保存で比較 OggMetadataWriter.swift CRCとpage走査をC++でまとめる余地はあるが、現行もCRCテーブル・unsafe pointer・まとめ読み書きを使用している。同じアルゴリズムの移植だけで大幅な改善を予測できない。CRC単体と編集全体を別に測る。

通常音声のtapは AudioEngineService.swift から表示時計と解析受付へ進み、ロック取得とPCMの配列化を伴う。これをC++で書き直しても、同じロックとコピーを残せばその費用は残る。時計共有と受け渡しの設計変更として比較し、AVAudioNodeTapBlockの実行スレッドと待機時間を測る。MTAudioProcessingTapのrender callbackと同じ実行条件だとは仮定しない。

移植の優先度が低い部分

音声デコードと書き出しはAVFoundation、SFBAudioEngine、FFmpeg、LAME等が主要な処理を担い、ラウドネス演算とFFTはAccelerateを利用している。Swiftの呼び出し側をC++に変えても、その中の演算は変更されない。ピッチ処理も既にC++であり、これらは先に処理時間の内訳を測る対象になる。

一覧の検索・ソートでは、比較のたびの文字列加工や数値変換を減らし、検索キーを再利用する方が直接的である。C++の文字列へ変換しても、日本語・英語、ロケール、Unicode、自然順ソートの要件は残る。SwiftUI、SwiftData、プレイリスト操作、LED/VU等の画面全体を移植する根拠は得られなかった。描画やモデル更新の回数を減らす変更と、言語変更の効果を混同しない。

比較時に保つ動作と採用条件

音声については、平均CPU時間だけでなく、callbackのp95/p99・観測最大値、締切超過、メモリ確保、参照管理、ロック待機を確認する。現行、Swiftで改善した版、同じ設計のC++版を、macOSとiPhone/iPad実機のRelease構成で比較する。有限回の観測最大値を最悪時間の保証とは扱わない。

  • EQの設定公開・読み取り・再確認・古い設定の回収を保持する。現行はhazard pointerでcallback中の設定の寿命を守り、設定変更側で解放する。C++の共有所有権へ置き換える場合も、callbackでの待機や最後の参照の解放が起きないことを別に確認する。
  • 再生世代と解析世代は音声の取得前に捕捉し、停止・シーク・一時停止・再開・曲切り替え・unprepare後に古い入力や結果を適用しない。ring満杯時の破棄と不連続通知を保持する。
  • 現行ringのconsumerは、サンプルを所有する配列へコピーしてからread indexを進める。配列をC++のspan等へ置き換え、index更新後もslotを借用するとproducerが上書きできる。コピーを除くなら、使用終了までslotを返さない仕組みなど、所有権も変更する必要がある。
  • mono/stereo、多チャンネル、interleaved/noninterleaved、実際のbuffer幅、複数sample rate、初回処理・設定変更・EQ再有効化を確認する。今回の数値比較は48 kHzの係数、stereo、限定した入力にとどまる。
  • メタデータ処理では、範囲・整数overflow・endian・CRCの検証、キャンセル、異常入力、原本保護、編集後の再読み込みを保持する。検証を削除して得た速度差を移植の成果としない。

既存の VideoEqualizerRealtimeTests、VideoAudioSampleRingTests、VideoSpectrumSourceTimelineTests、MP4/Oggの編集テスト等は、機能と競合の回帰確認に使える。言語間の速度差を測るテストではない。今回、製品のビルド・単体テスト・UIテスト・lintは再実行しておらず、実機の音切れ、消費電力、callbackの割り当て・ロックtraceも未計測である。

ビルドと参照資料

現行プロジェクトはC++に gnu++20、Swiftに言語モード6、Objective-C ARCとbridging headerを使用している。macOSのdeployment targetは26.5、iOS/iPadOSテスト設定は27.0で、候補の音声コードは両系統で共有される。提案は新しいOS APIを必須にするものではない。

既存のObjective-C窓口と .mm 内部実装を使う構成は、SwiftからC++型を直接importする構成とは別である。直接のC++ interoperabilityを採用する場合は対象のビルド設定を有効にし、境界の型と所有権を確認する。公式資料はC++ containerをSwiftから走査する際のcopyにも注意を促しており、vectorを公開するだけでコピー削減になるとは限らない。SwiftのC++相互運用ガイド、ビルド設定を参照。

AppleはSwiftでもInlineArray・Span等を使った確保・参照管理の削減方法を説明している。C++案の比較対象にこれらを含められるが、採用するmemberごとのOS可用性と実際の生成コードは確認する。Swiftのメモリと性能の改善の事例の倍率を、このアプリの期待値としては使わない。

選択SDKの MTAudioProcessingTap.h はprocessをリアルタイム処理とし、確保とブロッキング呼び出しを避けるよう求めている。process APIの契約を満たすかは、C++かSwiftかだけでは決まらない。

Clone this wiki locally