モバイルチームは Mac mini M4 あたりに「シミュレータを全部載せたい」と願いがちですが、オーバーサブスクライブの最初の兆候は CPU ではなく、圧縮メモリ、SpringBoard の起動が固まる現象、そして並列度を 1 つ下げると消える XCTest のタイムアウトです。本稿では、(1) メモリ階層ごとの安全な並列レーン数と、(2) 抑制かシャードかを切り替えるタイミング、という 2 つのマトリックスに加え、変更チケットに貼れる 8 ステップのロールアウト手順と、運用者が Slack を読み返さなくて済む FAQ 構造化データをまとめます。
2026 年時点では、Xcode 16 世代のランタイムと Apple Silicon M4 を前提にしています。UI テストでスクリーンショットを多用する前提の数字です。同一ホストでコンパイルも回しておりコンパイル専用プールがない場合は、表の並列数をさらに控えめにしてください。夜間バッチと昼間の PR 検証が同じプールに流れ込む設計では、昼間に見えていた余裕が夜間だけ消失する——典型的な「見かけ上のキャパシティ」問題です。キュー長を短く見せるために並列度だけ上げると、ページングストームが支配的になり、結果として p95 が悪化します。
キューを短くするのではなく、シミュレータ並列だけを増やしているサイン
- フレークが夜に集中する:ナイトリーパイプラインで 4 つの UI スイートが 1 ホストに重なる一方、ローカルではシミュレータ 2 台なら緑になる。
- SpringBoard や backboardd のウォッチドッグ終了が増えるのに、
xcodebuild自体はときどき成功と報告する。 - ディスクが急成長:
~/Library/Developer/CoreSimulator配下が膨らむ一方、CPU は 60% を下回る——メモリ圧力がページングとイメージの入れ替えを誘発している。
すでにバンドルをホスト間でシャードしている場合は、Mac mini M4 での UI テストシャーディングで水平分割の考え方を確認してください。本記事は、物理 1 台あたりの「縦方向の上限」を規定します。
監査やキャパシティ計画の文脈では、「何台のシミュレータを載せたか」より「何回クリーンなブートを行ったか」「圧縮メモリのピークが何 GB 続いたか」のほうが説明責任に耐えます。経営層向けには「CPU 使用率が低い=余裕」と誤解されやすいので、ユニファイドメモリの実測とディスク水位を同じダッシュボードに載せることを推奨します。
マトリックス A — ユニファイドメモリ階層と安全な並列シミュレータ本数
Xcode 16 世代のランタイム、Apple Silicon M4、スクリーンショット取得を伴う UI テストを想定。同一ホストでコンパイルも回す場合は行を一段階下げて読み替えてください。
| RAM | 並列 UI シミュレータ(同一 OS メジャー) | ユニットテストのみの並列シミュレータ | 打ち止めの症状 |
|---|---|---|---|
| 16 GB | 1(極小スイートなら 2 まで試行) | 厳格なティアダウンなら最大 3 | SpringBoard の再起動ループが時間あたり 2 を超える |
| 24 GB | スクリーンショット上限付きで UI レーン 2 | アイドル停止フック付きで 4 | 圧縮メモリが 5 GB を持続的に超える |
| 32 GB 以上 | コンパイルが分離されていれば UI レーン 3 | ジョブごとの RSS 予算付きなら最大 6 | スイート実行中に APFS 空きが 45 GB を下回る |
表はあくまで出発点です。アプリが Metal や大きなメモリマップを使う場合、シミュレータ 1 台あたりの常時 RSS が想定より大きくなります。そのときは「台数」ではなく「ジョブごとの RSS 上限」と「スクリーンショット枚数の上限」を先に締めます。逆に、ユニットのみで画面描画がほぼ無い場合は、表の右列を慎重に伸ばせますが、その場合も CoreSimulator のディスク膨張は別問題として監視が必要です。
マトリックス B — 抑制ポリシーとクリーンアップ頻度(✓ / ✗)
| ポリシー | RAM に効く | やりすぎるとスループットを損なう | 採用タイミング |
|---|---|---|---|
| ジョブごとにシミュレータをシャットダウン | ✓ | ✓ | コンパイル専用ホストが無い混合テナント系プール |
| パイプライン全体で起動したまま再利用 | ✗ | ✗ | 単一チーム専用ホストで日次パージ枠がある場合のみ |
| 利用不可デバイスを週次で消去 | ✓ | ✓ | 常時。ディスク保持指標とセット |
「毎ジョブで消す」と「長く再利用する」の中間として、ナイトリー前だけフル消去する運用もあります。重要なのは、消去ジョブが失敗したときに誰が気づくかです。10 分を超える消去は異常値としてアラートし、手動介入の Runbook に接続してください。
ページングが top ではなく CoreSimulator の内側に隠れる理由
アクティビティモニタには多数の小さめのプロセスが並びますが、シミュレータスタックはグラフィックスやファイルシステム向けの大きなバッキングストアを確保し、アプリインストール局面でスパイクします。Apple Silicon のユニファイドメモリでは、そのスパイクがコンパイルデーモンや Swift ランタイムキャッシュと競合します。実務的な修正は xcodebuild -parallel-testing-worker-number を上げることではなく、自組織の SKU に合ったマトリックス A の行と一致させることです。
リモート Mac フリートでは問題が増幅します。香港・日本・韓国・シンガポール・米国 にランナーがあるとき、開発者はレイテンシをボトルネックだと仮定しがちですが、東京の 16 GB RAM に載ったシミュレータ 4 台は、バージニアの 16 GB に載った 4 台と同じように振る舞います。地理はギガバイトを生み出しません。
並列計画は キュー SLO 向けプール設計と横断的に読み、「ui-heavy」ラベルが小さすぎるホストに決して着地しないようにオーケストレータを設定してください。
さらに踏み込むと、シミュレータのブート回数そのものがコストです。ブートはテストではなくインフラ作業であり、ブート頻度がマージ率を上回るなら、キューが詰まっているのではなくシミュレータを殺しすぎている可能性があります。ティアダウンを厳しくしすぎると逆効果になるため、マトリックス B の ✓/✗ をチームのテナント分離度合いに合わせて調整してください。
オーケストレータのラベルが現実を表す命名になっているか
macos-latest のような親しみやすいラベルは、キューが 16 GB なのか 32 GB なのかを隠します。プール名に RAM 階層とシミュレータ方針を含め、例えば mac-m4-24g-ui-max2 のようにすると、プロダクトチームがスクリーンショット重いスイートをコンパイル専用で買ったホストへ誤って流す事故を防げます。
ラベル変更とあわせて、時間あたりのシミュレータブート数をプロットするダッシュボードを用意してください。ブートがマージされた PR 数より速く増えるなら、コードをテストしているのではなくシミュレータを空回ししている状態です。経営層から「無害な Xcode アップデートのあとモバイル速度が謎に落ちた」と聞かれる前に抑制してください。
Runbook に一度書いておく数値デフォルト
- アイドルタイムアウト: XCTest トラフィックが 20 分無いシミュレータは電源オフ。
- リトライ上限: UI テストの PR あたりリトライは 2 回まで。システム的なメモリ圧力を隠蔽しない。
- ウォッチドッグ予算: 千テストあたり SpringBoard クラッシュが 0.5% を超えたら抑制のハードトリガー。
黄金律: 週次の CoreSimulator サイズがディスクガードを下回り、圧縮メモリがナイトリーで平坦になるまで、並列ワーカーは上げない。
8 つのロールアウトステップ
- 計測: 並列ワーカー数、p95 の UI ジョブ時間、CoreSimulator ディレクトリサイズのベースラインを取る。
- タグ付け: ホストを RAM 階層で分類し、16 GB に UI 重いラベルを明示承認なしで付けない。
- 実装: パイプラインのティアダウンにシャットダウンフックを入れ、成功パスだけでなく失敗パスにも。
- スケジュール: 週次消去ジョブを入れ、所要が 10 分を超えたらアラート。
- 文書化: ロールバックは並列を半分にする単一の環境フラグで足りるようにする。
- 教育: ローカルでシミュレータ 8 台動いたからといって CI のキャパシティ証明にならないことをモバイルエンジニアに伝える。
- ペアリング: スクリーンショットと動画成果物ポリシーを揃え、夜間にディスクが再充填されないようにする。
- スケールアウト: 抑制が妥当なのにキューが SLO を破るなら、NodeMac ホストを UI 専用として追加する。
8 ステップは変更管理の観点でも使いやすい粒度です。「計測なしの並列度変更」はほぼ必ずロールバック案件になるため、チケットにはベースライン数値を必ず貼り付けてください。ホスト追加の判断は、感情ではなく SLO 違反の持続時間とコストで行うと説明が通りやすくなります。
FAQ
Apple Silicon は Intel よりシミュレータを「勝手に」多く捌けるのか?
ワット当たりのスループットは良いですが、RAM は有限です。M4 は CPU の言い訳を減らすだけで、物理法則は変わりません。
シミュレータは DerivedData を共有してよいか?
ジョブごとに DerivedData ルートを分離し inode churn を抑えるのが無難です。キャッシュの統合はコンパイル専用ホストに限定してください。
SSH 中心のランナー運用の文献はどこか?
シミュレータ調整の前に、NodeMac の ヘルプセンターでリモートアクセスの型を確認してください。高レイテンシ回線の上で無理に画面操作まで SSH に寄せると、人為的なフレークが増えます。
正直なシミュレータ並列こそ、Apple Silicon M4 の GPU や Neural Engine が本来の実力を発揮する条件です。RAM のスラッシングが止まれば、UI テストは「謎のタイムアウト」ではなく実際に完了します。ネイティブ macOS 上で自動化に SSH、詰まった画面の確認に VNC を使うのは、デザイナーがローカルで行うのと同型の運用です。リージョンごとに専用 Mac mini M4 をレンタルして UI プールとコンパイルプールを分離すれば、同僚の共有 Jenkins Mac を再起動してくれと頼む必要は減ります。ホストあたり予測可能な RAM が説明責任の核です。マトリックスが「もう 1 台 UI 専用が要る」と示したら、無理に台数を積み増す前に リージョン別料金 を比較してください。