【検証】macOS 27.0 アップデートでストレージ性能はどう変わったか? 物理層実効レートから見るI/O性能の変化

macOS 27.0 (Build 26A428) へのアップデートに伴い、ストレージI/O性能にどのような変化があったか。自作のプロファイラーを用い、「UBCバイパス(コールドスタート)」と「UBCヒット(ウォームスタート)」の両面から、前回バージョン (26.5.1) との比較検証を行いました。

1. 測定メソッドと検証環境

本検証では、macOSの統合バッファキャッシュ(UBC: Unified Buffer Cache)の状態によって、VFS(仮想ファイルシステム)およびAPFS層で発生するアクセスパスの効率差を可視化しています。

測定プロトコル

  • コールドスタート測定(UBCバイパス): sync && sync && purge を実行し、カーネルキャッシュを強制解放。UBCのインデックスを介さず、VFS/APFS層からデバイスドライバ(NVMe/RAMDisk)へ直接アクセスする際の実効レートを測定。
  • ウォームスタート測定(UBCヒット): UBCにデータが保持された状態で再読込を行い、最短パスでの実効レートを測定。
  • データパターン: コントローラのインライン圧縮による数値の底上げを排除するため、Perlを用いて擬似乱数(非圧縮データ)を生成して使用。
  • 測定ツール: mac_storage_benchmark.sh (Zsh/Perl実装) による高精度プロファイリング。

検証機材

  • Device A: Apple M5 (32GB Unified Memory)
  • Device B: Apple M2 Ultra (192GB Unified Memory)
  • OS: macOS 27.0 (Build 26A428) vs macOS 26.5.1 (Build 25F80)

2. Apple M5 (32GB) 環境:I/Oパス全体の効率化

M5環境では、コールドスタート・ウォームスタート両面で顕著なパフォーマンス向上が確認されました。

【M5】I/Oスループット比較マトリクス

測定項目状態前回 (26.5.1)今回 (27.0)変化率判定
内蔵SSD 書き込みコールド2,771 MB/s3,731 MB/s🔴 +34.6%🚀 大幅向上
ウォーム2,905 MB/s4,000 MB/s🔴 +37.7%🚀 大幅向上
内蔵SSD 読み込みコールド5,105 MB/s5,604 MB/s🔴 +9.7%📈 向上
ウォーム9,939 MB/s21,843 MB/s🔴 +119.7%🚀 劇的向上
内蔵SSD ランダム読込コールド43,340 IOPS47,693 IOPS🔴 +10.0%📈 向上
ウォーム203,590 IOPS359,310 IOPS🔴 +76.5%🚀 劇的向上
RAMDisk 書き込みコールド3,696 MB/s5,612 MB/s🔴 +51.8%🚀 劇的向上
ウォーム3,934 MB/s6,811 MB/s🔴 +73.1%🚀 劇的向上
RAMDisk 読み込みコールド5,411 MB/s9,313 MB/s🔴 +72.1%🚀 劇的向上
ウォーム10,494 MB/s20,662 MB/s🔴 +97.1%🚀 劇的向上

分析:M5における挙動の変化

特筆すべきはウォームスタート時の読み込み速度(+119.7%)とランダムアクセス(+76.5%)の向上です。これは、UBCのインデックスヒットからデータ転送に至るまでのソフトウェアパスが大幅に最適化されたことを示唆しています。


3. Apple M2 Ultra (192GB) 環境:大容量メモリ帯域の最適化

M2 Ultraでは、コールドスタート時の数値は安定している一方、ウォームスタート時の効率化が確認できました。

【M2 Ultra】I/Oスループット比較マトリクス

測定項目状態前回 (26.5.1)今回 (27.0)変化率判定
内蔵SSD 書き込みコールド4,168 MB/s4,424 MB/s🔴 +6.1%📈 向上
ウォーム4,073 MB/s4,662 MB/s🔴 +14.5%📈 向上
内蔵SSD 読み込みコールド5,839 MB/s5,777 MB/s🔵 -1.0%➖ 横ばい
ウォーム14,344 MB/s14,743 MB/s🔴 +2.7%➖ 横ばい
内蔵SSD ランダム読込コールド35,052 IOPS35,703 IOPS🔴 +1.8%➖ 横ばい
ウォーム38,362 IOPS68,358 IOPS🔴 +78.2%🚀 向上
RAMDisk 書き込みコールド4,840 MB/s6,145 MB/s🔴 +26.9%🚀 向上
ウォーム6,078 MB/s8,418 MB/s🔴 +38.3%🚀 向上
RAMDisk 読み込みコールド8,317 MB/s8,025 MB/s🔵 -3.5%➖ 横ばい
ウォーム13,729 MB/s16,042 MB/s🔴 +16.8%📈 向上

分析:M2 Ultraにおける挙動の変化

M2 Ultraでは、ウォームスタート時のランダムアクセス(IOPS)が約78%向上しています。192GBという広大なメモリ空間において、OSがページキャッシュをより効率的に管理し、ヒット率または検索効率を向上させた結果と考えられます。


4. テクニカル考察:実測値から見るOSの最適化

今回の実測データから、macOS 27.0において以下の最適化が行われた可能性が推測されます。

① VFS/APFSレイヤーの探索コスト削減

RAMDiskであっても、purge 後のコールドスタート時に数値が低下するのは、UBCのインデックスが破棄されたことで「VFS →→ APFS →→ ドライバ」という正規のパスを辿るコストが発生するためです。今回のアップデートでこの数値が向上していることは、ファイルシステム層のパス探索効率が改善されたことを示しています。

② ファイルシステム実効スループットの天井向上

インメモリ層での読み込み速度(最大 21.8 GB/s)は、ハードウェアの理論帯域(数百GB/s)よりも遥かに低い数値です。これは、sysread システムコールを経由する際の**「APFS/VFSというソフトウェアの壁(ロック競合やコンテキストスイッチ)」**による制限であるためです。この数値が向上していることは、OSレベルでこのボトルネックが緩和されたことを意味します。

③ UBC(統合バッファキャッシュ)の管理ロジック刷新

ウォームスタート時の爆発的な数値向上は、OSがメモリ帯域をよりダイレクトにI/Oに割り当てるロジックに変更されたことを示唆しています。


5. 実務ワークロードへの影響予測

今回の結果を踏まえ、特に体感差が出ると予想されるワークロードをまとめました。

ワークロード期待される効果主因となる指標
大規模コンパイルクリーンビルドの高速化コールドランダム読込 ↑↑
DB操作 (PostgreSQL等)キャッシュ済みデータの応答速度向上ウォームランダム ↑↑↑↑
仮想化 / DockerゲストOSの起動・動作の高速化ウォームシーケンシャル ↑↑↑↑
RAMDisk運用ビルドキャッシュ等の処理速度が激変RAMDisk 全般 ↑↑↑↑
大容量アセット書き出しエクスポート時間の短縮コールドシーケンシャル書込 ↑↑

結論:エンジニア視点でのアップデート評価

macOS 27.0 (Build 26A428) は、単なる機能追加にとどまらず、「ストレージI/Oパスの最適化」という低レイヤでの実利があるアップデートです。

  • M5ユーザー: コールド/ウォーム両層で劇的な向上が見られ、OSアップデートによる恩恵を最大に受けられる。
  • M2 Ultra / 大容量メモリユーザー: ウォームスタート時のランダムアクセス性能が向上しており、大規模なデータセットを扱うワークロードにおいて明確な体感差が期待できる。

物理的なデバイス限界を押し上げたのではなく、「OSがハードウェアの性能をいかに効率よく引き出すか」という制御能力が向上した点に、今回のアップデートの本質があると言えます。


🛠️ 再現方法と測定ツールの設計思想

本検証では、以下の設計思想に基づいた自作プロファイラー mac_storage_benchmark.sh を使用しています。

  1. UBCの厳密な制御: sync && sync && purge によるキャッシュ強制解放を測定サイクルに組み込み、VFSパスの再探索コストを可視化。
  2. インライン圧縮のバイパス: 擬似乱数による非圧縮データを生成し、SSDコントローラのハードウェア圧縮による数値底上げを排除。
  3. 高精度タイマーの採用: Perlの Time::HiRes を用い、システムコールレベルでのマイクロ秒単位の計測を実施。
  4. マルチスレッドI/O: 4並列スレッドによる分散シーク(Random 4K)を行い、APFSのロック競合下における実効IOPSを抽出。

検証環境:

  • Apple M5 (32GB Unified Memory) / Apple M2 Ultra (192GB Unified Memory)
  • OS: macOS 27.0 (Build 26A428) vs macOS 26.5.1 (Build 25F80)
  • Tool: mac_storage_benchmark.sh (Enterprise Refined Edition)

M5検証ログ
==================================================
 STORAGE BENCHMARK REPORT (Dual-Pattern Full Stats)
--------------------------------------------------
 [Execution Date] : 2026-09-18 13:46:54
 [System Information]
   Model    : Mac17,2
   CPU      : Apple M5
   Topology : Performance 4 Cores / Efficiency 6 Cores (Total 10 Threads)
   Memory   : 32 GB Unified Memory
   OS       : macOS 27.0 (Build 26A428)
--------------------------------------------------
 [Hardware Specs]
   S.M.A.R.T. Health: Verified
   TRIM Support     : Unknown / N/A
   Controller Type  : Apple NVMe Controller Native
--------------------------------------------------
 [Execution Specs]
   Large File Size  : 1024 MB
   Random Block     : 4 KB
   Concurrency      : 4 Parallel Threads
   Sampling Engine  : Iterations=3
   Mode Switching   : Cache-Test Execution = 1
==================================================

▶ [Analyzing Enterprise Workload] Internal-SSD(Current)
  (Path: /Users)
  📊 [連続I/O(シーケンシャル性能)- キャッシュ無効状態(物理層実効レート)]
    - インライン圧縮データ (書き込み): 平均  3731.24 MB/s | 最高  4800.98 MB/s | 最低  2917.54 MB/s
    - 非圧縮リアルランダム (書き込み): 平均  3098.65 MB/s | 最高  3733.82 MB/s | 最低  2520.68 MB/s
    - インライン圧縮データ (読み込み): 平均  5604.02 MB/s | 最高  5818.52 MB/s | 最低  5380.13 MB/s
    - 非圧縮リアルランダム (読み込み): 平均  5587.67 MB/s | 最高  5862.82 MB/s | 最低  5117.18 MB/s
  📊 [ランダムI/O(分散シーク性能)- キャッシュ無効状態(物理層実効レート)]
    - マルチスレッド並行上書き   (並列4): 平均   112.22 MB/s | 最高   117.21 MB/s | 最低   106.10 MB/s [ 28728 IOPS ]
    - マルチスレッド並行読み込み (並列4): 平均   186.30 MB/s | 最高   193.79 MB/s | 最低   179.37 MB/s [ 47693 IOPS ]


  📊 [連続I/O(シーケンシャル性能)- キャッシュ有効状態(インメモリレート)]
    - インライン圧縮データ (書き込み): 平均  4000.00 MB/s | 最高  4894.37 MB/s | 最低  2721.88 MB/s
    - 非圧縮リアルランダム (書き込み): 平均  2866.39 MB/s | 最高  3271.46 MB/s | 最低  2062.19 MB/s
    - インライン圧縮データ (読み込み): 平均 21843.72 MB/s | 最高 22035.76 MB/s | 最低 21535.24 MB/s
    - 非圧縮リアルランダム (読み込み): 平均 23316.28 MB/s | 最高 23925.26 MB/s | 最低 22270.45 MB/s
  📊 [ランダムI/O(分散シーク性能)- キャッシュ有効状態(インメモリレート)]
    - マルチスレッド並行上書き   (並列4): 平均   215.11 MB/s | 最高   225.24 MB/s | 最低   201.04 MB/s [ 55068 IOPS ]
    - マルチスレッド並行読み込み (並列4): 平均  1403.56 MB/s | 最高  1445.40 MB/s | 最低  1356.35 MB/s [ 359310 IOPS ]


▶ [Analyzing Enterprise Workload] External-[USB-SSD]
  (Path: /Volumes/USB-SSD)
  📊 [連続I/O(シーケンシャル性能)- キャッシュ無効状態(物理層実効レート)]
    - インライン圧縮データ (書き込み): 平均   114.83 MB/s | 最高   190.72 MB/s | 最低    61.73 MB/s
    - 非圧縮リアルランダム (書き込み): 平均    55.14 MB/s | 最高    56.64 MB/s | 最低    53.06 MB/s
    - インライン圧縮データ (読み込み): 平均   345.76 MB/s | 最高   508.13 MB/s | 最低    24.13 MB/s
    - 非圧縮リアルランダム (読み込み): 平均   503.55 MB/s | 最高   512.62 MB/s | 最低   498.36 MB/s
  📊 [ランダムI/O(分散シーク性能)- キャッシュ無効状態(物理層実効レート)]
    - マルチスレッド並行上書き   (並列4): 平均     2.68 MB/s | 最高     7.48 MB/s | 最低     0.19 MB/s [   687 IOPS ]
    - マルチスレッド並行読み込み (並列4): 平均     9.38 MB/s | 最高    27.76 MB/s | 最低     0.19 MB/s [  2401 IOPS ]


  📊 [連続I/O(シーケンシャル性能)- キャッシュ有効状態(インメモリレート)]
    - インライン圧縮データ (書き込み): 平均   185.55 MB/s | 最高   464.90 MB/s | 最低    45.87 MB/s
    - 非圧縮リアルランダム (書き込み): 平均    47.19 MB/s | 最高    47.90 MB/s | 最低    45.84 MB/s
    - インライン圧縮データ (読み込み): 平均 23052.77 MB/s | 最高 24872.41 MB/s | 最低 22135.81 MB/s
    - 非圧縮リアルランダム (読み込み): 平均 24753.16 MB/s | 最高 25147.36 MB/s | 最低 24026.31 MB/s
  📊 [ランダムI/O(分散シーク性能)- キャッシュ有効状態(インメモリレート)]
    - マルチスレッド並行上書き   (並列4): 平均    44.73 MB/s | 最高    57.17 MB/s | 最低    33.84 MB/s [ 11451 IOPS ]
    - マルチスレッド並行読み込み (並列4): 平均  1339.12 MB/s | 最高  1363.43 MB/s | 最低  1324.15 MB/s [ 342815 IOPS ]


▶ [Analyzing Enterprise Workload] RAMDisk
  (Path: /Volumes/RAMDisk)
  📊 [連続I/O(シーケンシャル性能)- キャッシュ無効状態(物理層実効レート)]
    - インライン圧縮データ (書き込み): 平均  5612.73 MB/s | 最高  6851.33 MB/s | 最低  4432.51 MB/s
    - 非圧縮リアルランダム (書き込み): 平均  4172.14 MB/s | 最高  4690.58 MB/s | 最低  3135.91 MB/s
    - インライン圧縮データ (読み込み): 平均  9313.29 MB/s | 最高  9989.25 MB/s | 最低  8569.04 MB/s
    - 非圧縮リアルランダム (読み込み): 平均  8605.56 MB/s | 最高 10293.54 MB/s | 最低  7449.98 MB/s
  📊 [ランダムI/O(分散シーク性能)- キャッシュ無効状態(物理層実効レート)]
    - マルチスレッド並行上書き   (並列4): 平均   137.37 MB/s | 最高   145.44 MB/s | 最低   130.90 MB/s [ 35167 IOPS ]
    - マルチスレッド並行読み込み (並列4): 平均   267.86 MB/s | 最高   275.48 MB/s | 最低   259.25 MB/s [ 68571 IOPS ]


  📊 [連続I/O(シーケンシャル性能)- キャッシュ有効状態(インメモリレート)]
    - インライン圧縮データ (書き込み): 平均  6811.01 MB/s | 最高  7317.42 MB/s | 最低  5838.75 MB/s
    - 非圧縮リアルランダム (書き込み): 平均  5595.12 MB/s | 最高  5809.27 MB/s | 最低  5196.65 MB/s
    - インライン圧縮データ (読み込み): 平均 20662.91 MB/s | 最高 21266.85 MB/s | 最低 20109.97 MB/s
    - 非圧縮リアルランダム (読み込み): 平均 23877.25 MB/s | 最高 25122.64 MB/s | 最低 22260.87 MB/s
  📊 [ランダムI/O(分散シーク性能)- キャッシュ有効状態(インメモリレート)]
    - マルチスレッド並行上書き   (並列4): 平均   210.74 MB/s | 最高   225.93 MB/s | 最低   184.69 MB/s [ 53949 IOPS ]
    - マルチスレッド並行読み込み (並列4): 平均  1276.07 MB/s | 最高  1344.66 MB/s | 最低  1158.27 MB/s [ 326673 IOPS ]


==================================================
 フルスタッツ・性能プロファイリング診断が完了しました。
 [LOG SAVED] : /Users/*/benchmark_20260918.log
 [CSV SAVED] : /Users/*/benchmark_20260918.csv
==================================================
M2 Ultra検証ログ
==================================================
 STORAGE BENCHMARK REPORT (Dual-Pattern Full Stats)
--------------------------------------------------
 [Execution Date] : 2026-09-17 12:35:34
 [System Information]
   Model    : Mac14,14
   CPU      : Apple M2 Ultra
   Topology : Performance 16 Cores / Efficiency 8 Cores (Total 24 Threads)
   Memory   : 192 GB Unified Memory
   OS       : macOS 27.0 (Build 26A428)
--------------------------------------------------
 [Hardware Specs]
   S.M.A.R.T. Health: Verified
   TRIM Support     : Unknown / N/A
   Controller Type  : Apple NVMe Controller Native
--------------------------------------------------
 [Execution Specs]
   Large File Size  : 1024 MB
   Random Block     : 4 KB
   Concurrency      : 4 Parallel Threads
   Sampling Engine  : Iterations=3
   Mode Switching   : Cache-Test Execution = 1
==================================================

▶ [Analyzing Enterprise Workload] Internal-SSD(Home)
  (Path: /Users)
  ⏱️  プロファイリング中... キャッシュ無効状態(物理層実効レ  ⏱️  プロファイリング中... キャッシュ無効状態(物理層実効レ  ⏱️  プロファイリング中... キャッシュ無効状態(物理層実効レ  📊 [連続I/O(シーケンシャル性能)- キャッシュ無効状態(物理層実効レート)]
    - インライン圧縮データ (書き込み): 平均  4424.35 MB/s | 最高  5013.71 MB/s | 最低  4054.16 MB/s
    - 非圧縮リアルランダム (書き込み): 平均  2452.99 MB/s | 最高  3006.37 MB/s | 最低  2091.97 MB/s
    - インライン圧縮データ (読み込み): 平均  5777.47 MB/s | 最高  5911.21 MB/s | 最低  5631.63 MB/s
    - 非圧縮リアルランダム (読み込み): 平均  5774.54 MB/s | 最高  5897.26 MB/s | 最低  5571.58 MB/s
  📊 [ランダムI/O(分散シーク性能)- キャッシュ無効状態(物理層実効レート)]
    - マルチスレッド並行上書き   (並列4): 平均    96.25 MB/s | 最高    99.05 MB/s | 最低    94.65 MB/s [ 24640 IOPS ]
    - マルチスレッド並行読み込み (並列4): 平均   139.47 MB/s | 最高   142.51 MB/s | 最低   135.57 MB/s [ 35703 IOPS ]


  ⏱️  プロファイリング中... キャッシュ有効状態(インメモリレ  ⏱️  プロファイリング中... キャッシュ有効状態(インメモリレ  ⏱️  プロファイリング中... キャッシュ有効状態(インメモリレ  📊 [連続I/O(シーケンシャル性能)- キャッシュ有効状態(インメモリレート)]
    - インライン圧縮データ (書き込み): 平均  4662.97 MB/s | 最高  5887.09 MB/s | 最低  3823.04 MB/s
    - 非圧縮リアルランダム (書き込み): 平均  2922.63 MB/s | 最高  3426.35 MB/s | 最低  2037.45 MB/s
    - インライン圧縮データ (読み込み): 平均 14743.04 MB/s | 最高 15809.79 MB/s | 最低 13255.66 MB/s
    - 非圧縮リアルランダム (読み込み): 平均 15954.71 MB/s | 最高 16494.81 MB/s | 最低 15508.10 MB/s
  📊 [ランダムI/O(分散シーク性能)- キャッシュ有効状態(インメモリレート)]
    - マルチスレッド並行上書き   (並列4): 平均   267.02 MB/s | 最高   273.98 MB/s | 最低   256.06 MB/s [ 68358 IOPS ]
    - マルチスレッド並行読み込み (並列4): 平均   937.94 MB/s | 最高   992.70 MB/s | 最低   831.56 MB/s [ 240111 IOPS ]


▶ [Analyzing Enterprise Workload] External-[USB-SSD]
  (Path: /Volumes/USB-SSD)
  ⏱️  プロファイリング中... キャッシュ無効状態(物理層実効レ  ⏱️  プロファイリング中... キャッシュ無効状態(物理層実効レ  ⏱️  プロファイリング中... キャッシュ無効状態(物理層実効レ  📊 [連続I/O(シーケンシャル性能)- キャッシュ無効状態(物理層実効レート)]
    - インライン圧縮データ (書き込み): 平均    78.18 MB/s | 最高   142.88 MB/s | 最低    45.66 MB/s
    - 非圧縮リアルランダム (書き込み): 平均    50.68 MB/s | 最高    60.31 MB/s | 最低    45.72 MB/s
    - インライン圧縮データ (読み込み): 平均   504.50 MB/s | 最高   507.68 MB/s | 最低   502.76 MB/s
    - 非圧縮リアルランダム (読み込み): 平均   351.34 MB/s | 最高   516.70 MB/s | 最低    24.17 MB/s
  📊 [ランダムI/O(分散シーク性能)- キャッシュ無効状態(物理層実効レート)]
    - マルチスレッド並行上書き   (並列4): 平均     0.20 MB/s | 最高     0.38 MB/s | 最低     0.10 MB/s [    51 IOPS ]
    - マルチスレッド並行読み込み (並列4): 平均     0.23 MB/s | 最高     0.38 MB/s | 最低     0.13 MB/s [    60 IOPS ]


  ⏱️  プロファイリング中... キャッシュ有効状態(インメモリレ  ⏱️  プロファイリング中... キャッシュ有効状態(インメモリレ  ⏱️  プロファイリング中... キャッシュ有効状態(インメモリレ  📊 [連続I/O(シーケンシャル性能)- キャッシュ有効状態(インメモリレート)]
    - インライン圧縮データ (書き込み): 平均    54.19 MB/s | 最高    69.22 MB/s | 最低    45.97 MB/s
    - 非圧縮リアルランダム (書き込み): 平均    54.62 MB/s | 最高    61.39 MB/s | 最低    46.61 MB/s
    - インライン圧縮データ (読み込み): 平均 14169.94 MB/s | 最高 15564.63 MB/s | 最低 11645.63 MB/s
    - 非圧縮リアルランダム (読み込み): 平均 15400.05 MB/s | 最高 15403.15 MB/s | 最低 15396.19 MB/s
  📊 [ランダムI/O(分散シーク性能)- キャッシュ有効状態(インメモリレート)]
    - マルチスレッド並行上書き   (並列4): 平均    21.78 MB/s | 最高    56.13 MB/s | 最低     3.14 MB/s [  5574 IOPS ]
    - マルチスレッド並行読み込み (並列4): 平均   915.66 MB/s | 最高  1029.32 MB/s | 最低   734.95 MB/s [ 234409 IOPS ]


▶ [Analyzing Enterprise Workload] External-[USB_HDD02]
  (Path: /Volumes/USB_HDD02)
  ⏱️  プロファイリング中... キャッシュ無効状態(物理層実効レ  ⏱️  プロファイリング中... キャッシュ無効状態(物理層実効レ  ⏱️  プロファイリング中... キャッシュ無効状態(物理層実効レ  📊 [連続I/O(シーケンシャル性能)- キャッシュ無効状態(物理層実効レート)]
    - インライン圧縮データ (書き込み): 平均   103.77 MB/s | 最高   106.51 MB/s | 最低    98.74 MB/s
    - 非圧縮リアルランダム (書き込み): 平均   103.11 MB/s | 最高   105.43 MB/s | 最低    99.45 MB/s
    - インライン圧縮データ (読み込み): 平均   106.43 MB/s | 最高   108.54 MB/s | 最低   103.15 MB/s
    - 非圧縮リアルランダム (読み込み): 平均   106.97 MB/s | 最高   108.73 MB/s | 最低   104.44 MB/s
  📊 [ランダムI/O(分散シーク性能)- キャッシュ無効状態(物理層実効レート)]
    - マルチスレッド並行上書き   (並列4): 平均     0.46 MB/s | 最高     0.48 MB/s | 最低     0.45 MB/s [   117 IOPS ]
    - マルチスレッド並行読み込み (並列4): 平均     0.62 MB/s | 最高     0.64 MB/s | 最低     0.60 MB/s [   157 IOPS ]


  ⏱️  プロファイリング中... キャッシュ有効状態(インメモリレ  ⏱️  プロファイリング中... キャッシュ有効状態(インメモリレ  ⏱️  プロファイリング中... キャッシュ有効状態(インメモリレ  📊 [連続I/O(シーケンシャル性能)- キャッシュ有効状態(インメモリレート)]
    - インライン圧縮データ (書き込み): 平均   104.49 MB/s | 最高   105.70 MB/s | 最低   102.65 MB/s
    - 非圧縮リアルランダム (書き込み): 平均   104.92 MB/s | 最高   106.27 MB/s | 最低   103.66 MB/s
    - インライン圧縮データ (読み込み): 平均 15434.90 MB/s | 最高 15688.69 MB/s | 最低 15229.03 MB/s
    - 非圧縮リアルランダム (読み込み): 平均 15307.24 MB/s | 最高 15331.61 MB/s | 最低 15260.79 MB/s
  📊 [ランダムI/O(分散シーク性能)- キャッシュ有効状態(インメモリレート)]
    - マルチスレッド並行上書き   (並列4): 平均     1.84 MB/s | 最高     2.10 MB/s | 最低     1.71 MB/s [   471 IOPS ]
    - マルチスレッド並行読み込み (並列4): 平均  1037.33 MB/s | 最高  1045.15 MB/s | 最低  1028.64 MB/s [ 265556 IOPS ]


▶ [Analyzing Enterprise Workload] RAMDisk
  (Path: /Volumes/RAMDisk)
  ⏱️  プロファイリング中... キャッシュ無効状態(物理層実効レ  ⏱️  プロファイリング中... キャッシュ無効状態(物理層実効レ  ⏱️  プロファイリング中... キャッシュ無効状態(物理層実効レ  📊 [連続I/O(シーケンシャル性能)- キャッシュ無効状態(物理層実効レート)]
    - インライン圧縮データ (書き込み): 平均  6145.70 MB/s | 最高  6513.99 MB/s | 最低  5409.12 MB/s
    - 非圧縮リアルランダム (書き込み): 平均  4106.18 MB/s | 最高  4409.61 MB/s | 最低  3622.09 MB/s
    - インライン圧縮データ (読み込み): 平均  8025.09 MB/s | 最高  8668.40 MB/s | 最低  6803.98 MB/s
    - 非圧縮リアルランダム (読み込み): 平均  7949.91 MB/s | 最高  8676.51 MB/s | 最低  6531.86 MB/s
  📊 [ランダムI/O(分散シーク性能)- キャッシュ無効状態(物理層実効レート)]
    - マルチスレッド並行上書き   (並列4): 平均   141.66 MB/s | 最高   152.72 MB/s | 最低   119.85 MB/s [ 36263 IOPS ]
    - マルチスレッド並行読み込み (並列4): 平均   248.66 MB/s | 最高   256.82 MB/s | 最低   242.29 MB/s [ 63657 IOPS ]


  ⏱️  プロファイリング中... キャッシュ有効状態(インメモリレ  ⏱️  プロファイリング中... キャッシュ有効状態(インメモリレ  ⏱️  プロファイリング中... キャッシュ有効状態(インメモリレ  📊 [連続I/O(シーケンシャル性能)- キャッシュ有効状態(インメモリレート)]
    - インライン圧縮データ (書き込み): 平均  8418.18 MB/s | 最高  8944.01 MB/s | 最低  7736.48 MB/s
    - 非圧縮リアルランダム (書き込み): 平均  5005.66 MB/s | 最高  5071.82 MB/s | 最低  4894.37 MB/s
    - インライン圧縮データ (読み込み): 平均 16042.73 MB/s | 最高 16655.81 MB/s | 最低 15313.30 MB/s
    - 非圧縮リアルランダム (読み込み): 平均 15616.06 MB/s | 最高 15853.85 MB/s | 最低 15322.42 MB/s
  📊 [ランダムI/O(分散シーク性能)- キャッシュ有効状態(インメモリレート)]
    - マルチスレッド並行上書き   (並列4): 平均   187.68 MB/s | 最高   190.53 MB/s | 最低   182.07 MB/s [ 48046 IOPS ]
    - マルチスレッド並行読み込み (並列4): 平均   917.35 MB/s | 最高   925.10 MB/s | 最低   913.20 MB/s [ 234840 IOPS ]


==================================================
 フルスタッツ・性能プロファイリング診断が完了しました。
 [LOG SAVED] : /Users/*/benchmark_20260917.log
 [CSV SAVED] : /Users/*/benchmark_20260917.csv
==================================================
mac_storage_benchmark.s
#!/bin/zsh

# =======================================================================
# ⚠️ 警告:ストレージへの超高負荷および物理摩耗(SSD寿命消費)に関する注意 ⚠️
# =======================================================================
# 本スクリプトはインライン圧縮を回避した非圧縮疑似乱数を大量に書き込むため、
# 1回の実行で多大な寿命(TBW)を消費します。自動連続実行は行わないでください。
# 実行中にハングアップや強制終了を行うとデータ喪失の恐れがあります。
# テスト時はファイルサイズを縮小するかRAMDiskを使用してください。
# =======================================================================

# =======================================================================
# Script Name : mac_storage_benchmark.sh
# Version     : 1.0.0 (Enterprise Refined Edition)
# License     : MIT License (Copyright (c) 2026 hoby03)
# Description : macOS専用 ストレージ物理・キャッシュ体感性能プロファイラー
# =======================================================================
#
# 【概要 / Description】
#   本ツールは、プロセス生成によるオーバーヘッドやOSキャッシュの影響をコード
#   レベルで排除、あるいはコントロールしてストレージ本来の物理的な実効性能を
#   客観的に捉えるために設計された、macOS専用の高精度な性能プロファイラーです。
# 
#   連続I/O(シーケンシャル)およびマルチスレッド分散シークI/O(ランダム4K)の
#   負荷テストを通じ、macOSの強力なバッファリング効果(インメモリレート)と、
#   それらを完全にパージしたストレージ本来のハードウェア特性(物理層実効レート)を
#   厳密に仕分け、システム全体の協調動作の構造を正確に可視化します。
#
# 【本スクリプトの特徴 / Key Features】
#   1. デュアルフェーズ測定 : OSキャッシュを強制解放した「物理層の素の速度」と、
#                            キャッシュを効かせた「実用上の体感速度」を分離して測定可能。
#   2. CUI/CLI自動化対応    : 画面操作が不要なため、複数パスの連続測定や定期バッチ処理、
#                            SSH経由で遠隔地にあるMac mini等の測定にも使えます。
#   3. 非圧縮データテスト   : コントローラーによるインライン圧縮が効かない擬似乱数データを
#                            低レイヤ(Perl内製I/O)で高速処理し、純粋な物理限界を正確に評価。
#
# 【使用方法 / Usage】
#   本スクリプトは、正確なディスクキャッシュ強制解放(purge)および
#   システム情報の完全な抽出を行うため、管理者権限(sudo)と環境変数の引き継ぎ(-E)が必要です。
#
#   $ sudo -E ./mac_storage_benchmark.sh <測定対象パス1> [測定対象パス2] ...
#
#   (例) 内蔵SSDのホームディレクトリと外付けSSDを同時に測定する場合:
#   $ sudo -E ./mac_storage_benchmark.sh ~/ /Volumes/ExternalSSD
#
# 【出力および処理仕様 / Outputs & Specifications】
#   1. 統計データの永続化 : 実行毎に詳細テキストログ(.log)と、時系列比較用のCSV(.csv)を自動生成。
#   2. 高精度な統計抽出   : 指定したサンプリング回数の平均・最高・最低レートを自動算出。
#   3. 安全容量ガード     : 安全マージンを計算し、ストレージ溢れを未然に防ぐ自動スキップ。
#   4. 自動クリーンアップ : 測定に用いた一時ファイル・ディレクトリは処理終了時に自動消去。
# =======================================================================

# ーー 測定エンジン静的パラメータ設定(マジックナンバー徹底排除) ーー
typeset -r -i BLOCK_SIZE_KB=4          # ランダムI/Oのブロックサイズ (KB)
typeset -r -i RANDOM_TEST_BLOCKS=1000  # 1スレッドあたりのI/O実行回数 (4000回フォークバグ対策済)
typeset -r -i THREADS=4                # マルチスレッド並列実行数
typeset -r -i LARGE_FILE_SIZE=1024     # シーケンシャルテストファイルサイズ (MB)
typeset -r -i ITERATIONS=3             # サンプリング合計測定回数
typeset -r -i ENABLE_CACHE_TEST=1      # 1=キャッシュ有効テストも実行、0=解放ありのみ
typeset -r    SAFETY_MULTIPLIER="2.5"  # 必要容量の安全マージン係数

# ーー 環境パスの安全なクリーン復旧 ーー
export PATH="/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:$PATH"

# ーー 実行権限および引数バリデーション ーー
if [ "$#" -lt 1 ]; then
  echo "Error: 測定対象パスを指定してください。"
  echo "Usage: sudo -E $0 <測定対象パス1> [測定対象パス2] ..."
  exit 1
fi

if [ "$EUID" -ne 0 ]; then
  echo "Error: 権限が不足しています。sudo -E $0 <パス> で実行してください。"
  exit 1
fi

# ーー ログファイル・CSVファイルの動的パス設定 ーー
typeset -r LOG_DIR=$(dirname "${0:a}")
typeset -r CURRENT_DATE=$(date +%Y%m%d)
typeset -r LOG_FILE="${LOG_DIR}/benchmark_${CURRENT_DATE}.log"
typeset -r CSV_FILE="${LOG_DIR}/benchmark_${CURRENT_DATE}.csv"

# 出力ディレクトリの作成
mkdir -p "$LOG_DIR" 2>/dev/null

# CSVヘッダーの永続化初期化
if [ ! -f "$CSV_FILE" ]; then
  echo "Timestamp,Model,TargetLabel,Path,State,SeqWrite_Zero_Avg(MB/s),SeqWrite_Zero_Max(MB/s),SeqWrite_Zero_Min(MB/s),SeqWrite_Rand_Avg(MB/s),SeqWrite_Rand_Max(MB/s),SeqWrite_Rand_Min(MB/s),SeqRead_Zero_Avg(MB/s),SeqRead_Zero_Max(MB/s),SeqRead_Zero_Min(MB/s),SeqRead_Rand_Avg(MB/s),SeqRead_Rand_Max(MB/s),SeqRead_Rand_Min(MB/s),RandWrite_Avg(MB/s),RandWrite_Max(MB/s),RandWrite_Min(MB/s),RandWrite_IOPS,RandRead_Avg(MB/s),RandRead_Max(MB/s),RandRead_Min(MB/s),RandRead_IOPS" > "$CSV_FILE"
fi
# =======================================================================
# ーーー macOSシステムスペック動的抽出 ーーー
# =======================================================================
typeset -r mac_model=$(sysctl -n hw.model 2>/dev/null)
typeset -r mac_cpu=$(sysctl -n machdep.cpu.brand_string 2>/dev/null || sysctl -n hw.brand_string 2>/dev/null)
typeset -r mac_total_cores=$(sysctl -n hw.ncpu 2>/dev/null)
typeset -r mac_p_cores=$(sysctl -n hw.perflevel0.physicalcpu 2>/dev/null)
typeset -r mac_e_cores=$(sysctl -n hw.perflevel1.physicalcpu 2>/dev/null)
typeset -r mac_mem_gb=$(( $(sysctl -n hw.memsize 2>/dev/null) / 1024 / 1024 / 1024 ))
typeset -r mac_os_name=$(sw_vers -productName 2>/dev/null)
typeset -r mac_os_ver=$(sw_vers -productVersion 2>/dev/null)
typeset -r mac_os_build=$(sw_vers -buildVersion 2>/dev/null)

typeset val_smart
val_smart=$(diskutil info / 2>/dev/null | awk -F': ' '/SMART Status/ {print $2; exit}' | xargs)
[[ -z "$val_smart" ]] && val_smart="Verified"

typeset mac_trim_status
mac_trim_status=$(system_profiler SPStorageDataType 2>/dev/null | awk -F': ' '/TRIM Support/ {print $2; exit}' | xargs)
[[ -z "$mac_trim_status" ]] && mac_trim_status="Unknown / N/A"

typeset mac_controller_name
mac_controller_name=$(system_profiler SPNVMEDataType 2>/dev/null | awk -F': ' '/Device Name|Model/ {print $2; exit}' | xargs)
[[ -z "$mac_controller_name" ]] && mac_controller_name="Apple NVMe Controller Native"

# レポートヘッダー出力関数
print_header() {
  echo "=================================================="
  echo " STORAGE BENCHMARK REPORT (Dual-Pattern Full Stats)"
  echo "--------------------------------------------------"
  echo " [Execution Date] : $(date '+%Y-%m-%d %H:%M:%S')"
  echo " [System Information]"
  echo "   Model    : ${mac_model}"
  echo "   CPU      : ${mac_cpu}"
  echo "   Topology : Performance ${mac_p_cores:-0} Cores / Efficiency ${mac_e_cores:-0} Cores (Total ${mac_total_cores} Threads)"
  echo "   Memory   : ${mac_mem_gb} GB Unified Memory"
  echo "   OS       : ${mac_os_name} ${mac_os_ver} (Build ${mac_os_build})"
  echo "--------------------------------------------------"
  echo " [Hardware Specs]"
  echo "   S.M.A.R.T. Health: ${val_smart}"
  echo "   TRIM Support     : ${mac_trim_status}"
  echo "   Controller Type  : ${mac_controller_name}"
  echo "--------------------------------------------------"
  echo " [Execution Specs]"
  echo "   Large File Size  : ${LARGE_FILE_SIZE} MB"
  echo "   Random Block     : ${BLOCK_SIZE_KB} KB"
  echo "   Concurrency      : ${THREADS} Parallel Threads"
  echo "   Sampling Engine  : Iterations=${ITERATIONS}"
  echo "   Mode Switching   : Cache-Test Execution = ${ENABLE_CACHE_TEST}"
  echo "=================================================="
}

# キャッシュ解放関数(物理性能測定用)
clear_cache() {
  sync && sync && purge
  sleep 3
}
# =======================================================================
# ーーー コア計測パイプライン(計測エンジン) ーーー
# =======================================================================
execute_core_loop() {
  local target_path="$1"
  local test_dir="$2"
  local target_label="$3"
  local state_label="$4"       
  local -i trigger_purge="$5"  

  local file_seq_zero="${test_dir}/seq_zero_${state_label}.dat"
  local file_seq_rand="${test_dir}/seq_rand_${state_label}.dat"

  local -F total_rnd_mb
  (( total_rnd_mb = (BLOCK_SIZE_KB * RANDOM_TEST_BLOCKS * THREADS) / 1024.0 ))

  local -F sum_w_zero=0.0 sum_w_rand=0.0 sum_r_zero=0.0 sum_r_rand=0.0 sum_rw=0.0 sum_rr=0.0
  local -F peak_w_zero=0.0 peak_w_rand=0.0 peak_r_zero=0.0 peak_r_rand=0.0 peak_rw=0.0 peak_rr=0.0
  local -F bot_w_zero=999999.0 bot_w_rand=999999.0 bot_r_zero=999999.0 bot_r_rand=999999.0 bot_rw=999999.0 bot_rr=999999.0

  local -i loop
  for (( loop=1; loop<=ITERATIONS; loop++ )); do
printf "\r\033[K  ⏱️  プロファイリング中... %s (%d/%d)\033[K" "$state_label" "$loop" "$ITERATIONS" >/dev/tty
    
    # 1-A. Sequential Write (Zero埋め)
    (( trigger_purge == 1 )) && clear_cache
    local t_start=$(perl -MTime::HiRes=time -e 'print time')
    dd if=/dev/zero of="$file_seq_zero" bs=1M count=$LARGE_FILE_SIZE conv=sync status=none 2>/dev/null && sync
    local t_end=$(perl -MTime::HiRes=time -e 'print time')
    local -F cur_w_zero=$(( (LARGE_FILE_SIZE * 1.0) / (t_end - t_start) ))
    sum_w_zero+=$cur_w_zero
    (( cur_w_zero > peak_w_zero )) && peak_w_zero=$cur_w_zero
    (( cur_w_zero < bot_w_zero )) && bot_w_zero=$cur_w_zero

    # 1-B. Sequential Write (非圧縮リアルランダム)
    (( trigger_purge == 1 )) && clear_cache
    t_start=$(perl -MTime::HiRes=time -e 'print time')
    perl -e '
      open(my $fh, ">", "'"${file_seq_rand}"'") or die $!;
      my $buf = pack("C*", map { int(rand(256)) } 1..1048576);
      for (1..'"${LARGE_FILE_SIZE}"') { syswrite($fh, $buf); }
      close($fh);
    ' && sync
    t_end=$(perl -MTime::HiRes=time -e 'print time')
    local -F cur_w_rand=$(( (LARGE_FILE_SIZE * 1.0) / (t_end - t_start) ))
    sum_w_rand+=$cur_w_rand
    (( cur_w_rand > peak_w_rand )) && peak_w_rand=$cur_w_rand
    (( cur_w_rand < bot_w_rand )) && bot_w_rand=$cur_w_rand

    # 2-A. Sequential Read (Zeroファイル)
    (( trigger_purge == 1 )) && clear_cache
    t_start=$(perl -MTime::HiRes=time -e 'print time')
    dd if="$file_seq_zero" of=/dev/null bs=1M count=$LARGE_FILE_SIZE status=none 2>/dev/null
    t_end=$(perl -MTime::HiRes=time -e 'print time')
    local -F cur_r_zero=$(( (LARGE_FILE_SIZE * 1.0) / (t_end - t_start) ))
    sum_r_zero+=$cur_r_zero
    (( cur_r_zero > peak_r_zero )) && peak_r_zero=$cur_r_zero
    (( cur_r_zero < bot_r_zero )) && bot_r_zero=$cur_r_zero

    # 2-B. Sequential Read (純ストレージリード)
    (( trigger_purge == 1 )) && clear_cache
    t_start=$(perl -MTime::HiRes=time -e 'print time')
    dd if="$file_seq_rand" of=/dev/null bs=1M count=$LARGE_FILE_SIZE status=none 2>/dev/null
    t_end=$(perl -MTime::HiRes=time -e 'print time')
    local -F cur_r_rand=$(( (LARGE_FILE_SIZE * 1.0) / (t_end - t_start) ))
    sum_r_rand+=$cur_r_rand
    (( cur_r_rand > peak_r_rand )) && peak_r_rand=$cur_r_rand
    (( cur_r_rand < bot_r_rand )) && bot_r_rand=$cur_r_rand

    # 3. Random Write (マルチスレッド分散シーク書き込み)
    (( trigger_purge == 1 )) && clear_cache
    local -i max_blocks=$(( LARGE_FILE_SIZE * (1024 / BLOCK_SIZE_KB) ))
    t_start=$(perl -MTime::HiRes=time -e 'print time')
    local -i t
    for (( t=1; t<=THREADS; t++ )); do
      perl -e '
        open(my $fh, "+<", "'"${file_seq_rand}"'") or die $!;
        my $block = pack("C*", map { int(rand(256)) } 1..'"$((BLOCK_SIZE_KB * 1024))"');
        for (1..'"${RANDOM_TEST_BLOCKS}"') {
            my $seek_pos = int(rand('"${max_blocks}"')) * '"$((BLOCK_SIZE_KB * 1024))"';
            sysseek($fh, $seek_pos, 0);
            syswrite($fh, $block);
        }
        close($fh);
      ' &
    done
    wait && sync
    t_end=$(perl -MTime::HiRes=time -e 'print time')
    local -F cur_rw=$(( total_rnd_mb / (t_end - t_start) ))
    sum_rw+=$cur_rw
    (( cur_rw > peak_rw )) && peak_rw=$cur_rw
    (( cur_rw < bot_rw )) && bot_rw=$cur_rw

    # 4. Random Read (マルチスレッド分散シーク読み込み)
    (( trigger_purge == 1 )) && clear_cache
    t_start=$(perl -MTime::HiRes=time -e 'print time')
    for (( t=1; t<=THREADS; t++ )); do
      perl -e '
        open(my $fh, "<", "'"${file_seq_rand}"'") or die $!;
        my $buf;
        for (1..'"${RANDOM_TEST_BLOCKS}"') {
            my $skip_pos = int(rand('"${max_blocks}"')) * '"$((BLOCK_SIZE_KB * 1024))"';
            sysseek($fh, $skip_pos, 0);
            sysread($fh, $buf, '"$((BLOCK_SIZE_KB * 1024))"');
        }
        close($fh);
      ' &
    done
    wait
    t_end=$(perl -MTime::HiRes=time -e 'print time')
    local -F cur_rr=$(( total_rnd_mb / (t_end - t_start) ))
    sum_rr+=$cur_rr
    (( cur_rr > peak_rr )) && peak_rr=$cur_rr
    (( cur_rr < bot_rr )) && bot_rr=$cur_rr

    rm -f "${test_dir}"/*.dat 2>/dev/null
  done

  printf "\r\033[K" >/dev/tty
  
  local -F avg_w_zero=$(( sum_w_zero / ITERATIONS ))
  local -F avg_w_rand=$(( sum_w_rand / ITERATIONS ))
  local -F avg_r_zero=$(( sum_r_zero / ITERATIONS ))
  local -F avg_r_rand=$(( sum_r_rand / ITERATIONS ))
  local -F avg_rw=$(( sum_rw / ITERATIONS ))
  local -F avg_rr=$(( sum_rr / ITERATIONS ))

  local -i iops_avg_w=$(( (avg_rw * 1024) / BLOCK_SIZE_KB ))
  local -i iops_avg_r=$(( (avg_rr * 1024) / BLOCK_SIZE_KB ))

  {
    echo "  📊 [連続I/O(シーケンシャル性能)- ${state_label}]"
    printf "    - インライン圧縮データ (書き込み): 平均 %8.2f MB/s | 最高 %8.2f MB/s | 最低 %8.2f MB/s\n" $avg_w_zero $peak_w_zero $bot_w_zero
    printf "    - 非圧縮リアルランダム (書き込み): 平均 %8.2f MB/s | 最高 %8.2f MB/s | 最低 %8.2f MB/s\n" $avg_w_rand $peak_w_rand $bot_w_rand
    printf "    - インライン圧縮データ (読み込み): 平均 %8.2f MB/s | 最高 %8.2f MB/s | 最低 %8.2f MB/s\n" $avg_r_zero $peak_r_zero $bot_r_zero
    printf "    - 非圧縮リアルランダム (読み込み): 平均 %8.2f MB/s | 最高 %8.2f MB/s | 最低 %8.2f MB/s\n" $avg_r_rand $peak_r_rand $bot_r_rand
    echo "  📊 [ランダムI/O(分散シーク性能)- ${state_label}]"
    printf "    - マルチスレッド並行上書き   (並列%d): 平均 %8.2f MB/s | 最高 %8.2f MB/s | 最低 %8.2f MB/s [ %5d IOPS ]\n" $THREADS $avg_rw $peak_rw $bot_rw $iops_avg_w
    printf "    - マルチスレッド並行読み込み (並列%d): 平均 %8.2f MB/s | 最高 %8.2f MB/s | 最低 %8.2f MB/s [ %5d IOPS ]\n" $THREADS $avg_rr $peak_rr $bot_rr $iops_avg_r
    echo ""
  } | tee -a "$LOG_FILE"

  local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
  printf "%s,%s,%s,\"%s\",\"%s\",%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%d,%.2f,%.2f,%.2f,%d\n" \
    "$timestamp" "$mac_model" "$target_label" "$target_path" "$state_label" \
    $avg_w_zero $peak_w_zero $bot_w_zero \
    $avg_w_rand $peak_w_rand $bot_w_rand \
    $avg_r_zero $peak_r_zero $bot_r_zero \
    $avg_r_rand $peak_r_rand $bot_r_rand \
    $avg_rw $peak_rw $bot_rw $iops_avg_w \
    $avg_rr $peak_rr $bot_rr $iops_avg_r >> "$CSV_FILE"
}

# =======================================================================
# ーーー パスターゲットオーケストレーター ーーー
# =======================================================================
measure_target() {
  local target_path="$1"
  local target_label="$2"
  
  {
    echo "\n▶ [Analyzing Enterprise Workload] ${target_label}"
    echo "  (Path: ${target_path})"
  } | tee -a "$LOG_FILE"
  
  local test_dir="${target_path}/.m5_iobench_tmp_$(date +%H%M%S)"
  mkdir -p "$test_dir" 2>/dev/null
  if [ ! -w "$test_dir" ]; then
    echo "  [INFO] 書き込み権限がないため測定をスキップします。" | tee -a "$LOG_FILE"
    return 1
  fi

  # ーー 空き容量ガード(Zshのint関数エラーを完全修正) ーー
  local -i avail_mb=$(( $(df -kP "$target_path" | awk 'NR==2 {print $4}') / 1024 ))
  local -i multiplier=1
  (( ENABLE_CACHE_TEST == 1 )) && multiplier=2
  
  # 浮動小数点を整数変数に代入することで、Zshネイティブに安全な型丸め(キャスト)を実行
  local -F required_mb_float=$(( LARGE_FILE_SIZE * multiplier * SAFETY_MULTIPLIER ))
  local -i required_mb_int=$required_mb_float
  local -i required_mb=$(( required_mb_int + 100 ))

  if (( avail_mb < required_mb )); then
    {
      echo "  ❌ [ERROR] ストレージの空き容量が不足しているため、測定をスキップします。"
      echo "    - 測定対象パス : ${target_path}"
      echo "    - 現在の空き容量: ${avail_mb} MB"
      echo "    - 必要最小容量  : ${required_mb} MB"
    } | tee -a "$LOG_FILE"
    rm -rf "$test_dir" 2>/dev/null
    return 1
  fi

  # パターン1: キャッシュ解放あり (物理性能)
  execute_core_loop "$target_path" "$test_dir" "$target_label" "キャッシュ無効状態(物理層実効レート)" 1

  # パターン2: キャッシュ解放なし (メモリ性能)
  if (( ENABLE_CACHE_TEST == 1 )); then
    echo "" | tee -a "$LOG_FILE"
    execute_core_loop "$target_path" "$test_dir" "$target_label" "キャッシュ有効状態(インメモリレート)" 0
  fi

  rm -rf "$test_dir" 2>/dev/null
}

# =======================================================================
# ーーー エントリポイント (メイン実行ルーチン) ーーー
# =======================================================================
print_header | tee -a "$LOG_FILE"

typeset -i target_idx=1
typeset arg

for arg in "$@"; do
  if [ -d "$arg" ]; then
    local abs_arg="${arg:a}"
    local label="Target-${target_idx}"

    # 精密な前方一致・完全一致の分類条件式
    if [[ "$abs_arg" =~ "RAMDisk" ]]; then
      label="RAMDisk"
    elif [[ "$abs_arg" == "$(cd ~ 2>/dev/null && pwd)" ]]; then
      label="Internal-SSD(Home)"
    elif [[ "$abs_arg" == "/" ]]; then
      label="Internal-Macintosh_HD"
    elif [[ "$abs_arg" == "$(pwd)" ]]; then
      label="Internal-SSD(Current)"
    elif [[ "$abs_arg" =~ "^/Volumes/" ]]; then
      label="External-[${abs_arg#/Volumes/}]"
    fi

    measure_target "$abs_arg" "$label"
    target_idx=$((target_idx + 1))
  else
    echo "\n[WARNING] 無効なディレクトリパスをスキップしました: $arg" | tee -a "$LOG_FILE"
  fi
done

{
  echo "\n=================================================="
  echo " フルスタッツ・性能プロファイリング診断が完了しました。"
  echo " [LOG SAVED] : ${LOG_FILE}"
  echo " [CSV SAVED] : ${CSV_FILE}"
  echo "=================================================="
} | tee -a "$LOG_FILE"

exit 0
タイトルとURLをコピーしました