Share
Embed
仮想ファイルシステムを導入して開発環境のストレージ課題を解消する
CEDEC 2024 にて講演した『仮想ファイルシステムを導入して開発環境のストレージ課題を解消する』の資料です。
株式会社セガ 開発技術部 山田 英伸
株式会社セガ 第3オンライン研究開発プログラム1部 高橋 陸
株式会社セガ フェイブ AMインフラ研究開発部 阿部輝仁
SEGADevTech
August 27, 2024
Transcript
87 slides
-
2 ©SEGA 自己紹介 株式会社セガ 技術本部 開発技術部 山田 英伸 SEGA入社後、業務用ゲームタイトルの下回りライブラリ開発やツール 作成などに従事。現在は、特定の専門分野を持たず、幅広い領域で色々
とやっています 講演歴 - Unite Tokyo 2019 「大量のアセットも怖くない~HTTP/2による高速な通信の実装例」 - CEDEC 2021 「ダウンロード時間を大幅減!~大量のアセットをさばく高速な 実装と運用事例の共有~」 -
3 ©SEGA 自己紹介 株式会社セガ 第3事業部 第3オンライン研究開発プログラム1部 高橋 陸 『ファンタシースターオンライン2』ではアクションパートを担当。 『PSO2
ニュージェネシス』ではCI/CD管理、デプロイ業務に従事 -
6 ©SEGA • 3分でわかる講演の要点 • 仮想ファイルシステムとは • ProjFS と SlimSync
• 『PSO2 ニュージェネシス』開発環境での効果 • ProjFS の組み込み TIPS • まとめ アジェンダ -
8 ©SEGA • ゲーム開発の規模が大きく – アセットの種類・サイズが増加 – バージョン違いによる並行開発 開発環境の現状と課題 開発に必要なデータを置いておくため、
開発ストレージは常にギリギリで、負荷が大きい状況 -
9 ©SEGA • 「SlimSync」: リポジトリ同期ツール – Windows用 仮想ファイルシステムを活用 – バージョン管理下の全ファイルが「ある」ように見える
ツールを開発 リポジトリからのデータ取得を仮想ファイルシステムによって オンデマンド化&シームレス化し、開発者のリポジトリ同期に 要する時間やストレージ容量を節約するツール -
12 ©SEGA • 効果 – 実際のプロジェクトでの検証結果を共有 • これから実装する人向け – 仮想ファイルシステム
• 動作の概要と仕組み – 実装や取扱いの注意点 講演のポイント -
14 ©SEGA • 「仮想ファイルシステム」でイメージするもの – 仮想マシンの “仮想ハードディスク” – Unix系OSのVFS (Virtual
File System) – 仮想ファイルを扱うもの • ネットワーク越しに扱うもの – NFS, CIFS, クラウドストレージなど 仮想ファイルシステムとは -
15 ©SEGA • Unix系 VFS の場合 – 普段は一番上の状態を見ている – 各ディレクトリの
ファイルシステムを気にしない 仮想ファイルシステムとは ext4 proc Virtual File System xfs Disk Disk /home /var /proc -
16 ©SEGA • Overlay FS (コンテナ型仮想化でよく登場する) – コンテナ内で見えるファイルシステムとして使用 – ディレクトリを重ね合わせた状態を見せる
仮想ファイルシステムとは F1 F2 F3 F2 Lower Upper F1 F3 F2 Merged -
17 ©SEGA • Windows特有で使えるもの – Projected File System API –
Cloud File API • ファイルの存在を見せること、 データ本体を切り離すこと、を実現 仮想ファイルシステムとは File Data File Data Data 本講演では、これらに対して 仮想ファイルシステムと呼びます -
18 ©SEGA • データ(ファイル)は – ユーザーによって必要な範囲が変わる – 全ファイルが必要なことは稀 仮想ファイルシステムが効果的な理由 File1
File2 Data Data File3 Data File4 Data File1 File2 File3 Data File4 Data Data Data ユーザー1 ユーザー2 つかう分だけデータ実体があればよい! -
19 ©SEGA • データ本体がない状態でも、必要になったら”自動”で取得 仮想ファイルシステムが効果的な理由 File1 File2 Data Data File3
Data File4 Data ユーザー File5 File6 Data File7 Data File8 Data Data Data Data Data 全てが揃っているように見えて、 必要な分だけのデータでストレージ消費を最小化 アクセスの瞬間で中身を取得 (実体化) -
21 ©SEGA • ProjFS を採用 – ファイル/ディレクトリの外観が使用前後で変化しない – 安定した環境ならProjFSは高速 ProjFS
と Cloud File API OS標準 速度 異常状態への 対応 その他 ProjFS 〇 ◎ × GVFS(Git Virtual File System)は ProjFS の先祖 C#ラッパーがある Cloud File API 〇 ▲ 〇 クラウドストレージに対応した仮想ファイル機 能を提供 -
22 ©SEGA • Windows10 (1803) にてProjFSの可能性を予見(動作をチェック済) – https://learn.microsoft.com/ja-jp/windows/win32/projfs/projected-file-system • Virtual
Sync: Terabytes on Demand (GDC2022 / Bungie, Inc) 講演 – https://www.gdcvault.com/play/1027699/Virtual-Sync-Terabytes-on • 『NGS』 開発チームと前述の講演をきっかけに協力体制 ProjFS採用の理由 -
23 ©SEGA • Git Virtual File System: GVFS – 2018年ごろに話題
– Team Foundation , Azure DevOps Server が必要 – 現在は “VFS for Git” (ただしメンテナンスモード) 余談 -
24 ©SEGA User Mode Kernel Mode 初期化: PrjStartVirtualizing() • 仮想化を開始するパスを設定
• 各処理のコールバックを登録 ProjFS APIの使い方 指定フォルダー配下の特定 I/O 要求のみ転送 D:¥my-project¥ ProjFS [Provider] StartEnumeration GetPlaceholderInfo GetFileData : ProjFS [System] StartEnumeration GetPlaceholderInfo GetFileData : -
25 ©SEGA User Mode Kernel Mode 初期化: PrjStartVirtualizing() • 仮想化を開始するパスを設定
• 各処理のコールバックを登録 ProjFS APIの使い方 指定フォルダー配下の特定 I/O 要求のみ転送 D:¥my-project¥ ProjFS [Provider] StartEnumeration GetPlaceholderInfo GetFileData : ProjFS [System] StartEnumeration GetPlaceholderInfo GetFileData : PrjFillDirEntryBuffer() PrjWritePlaceholderInfo () PrjWriteFileData() -
26 ©SEGA • ProjFS制御下では、ファイルは以下の状態を遷移 ファイルの状態変化 項目がDisk上にない. アイテム列挙に出現 FindFirstFile, readdir など
項目はDisk上に存在. メタデータのみで、 ファイル内容はカラ オープン操作 CreateFile, fopen など 項目はDisk上に存在. ファイル内容が ストレージ上に存在. 読み取り操作 ReadFile, fread など Hydratedな状態から ファイル内容が変更 書込み操作 WriteFile, fwrite など Virtual Placeholder open read Hydrated Full -
27 ©SEGA • ProjFS制御下では、ファイルは以下の状態を遷移 ファイルの状態変化 Virtual Placeholder Hydrated Full open
仮想状態 実体化 ストレージを消費する範囲 項目がDisk上にない. アイテム列挙に出現 項目はDisk上に存在. メタデータのみで、 ファイル内容はカラ 項目はDisk上に存在. ファイル内容が ストレージ上に存在. Hydratedな状態から ファイル内容が変更 read write -
29 ©SEGA User Mode Kernel Mode • ドライバ間に、フィルタドライバーが挿入可能 – ProjFSはフィルタドライバーの1種
• ProjFSを使うと ユーザーモードのプログラムとして実装 ProjFSの仕組み ProjFS ドライバー NTFS ファイルシステムドライバー エクスプローラー など ランタイムライブラリ ファイルシステムドライバー ストレージクラスドライバー HDD/SSD ProjFSを利用した実装 ・列挙情報 ・ファイル内容操作 .... Callback I/Oリクエスト データ -
30 ©SEGA ProjFSの仕組み (例:リポジトリ投影) Callback ProjFS ドライバー NTFS ファイルシステムドライバー エクスプローラー
など ランタイムライブラリ ファイルシステムドライバー ストレージクラスドライバー HDD/SSD project/ • main/ • Readme.txt ストレージより project/ • branch-a/ • License.txt リポジトリ情報より 上層に見せる情報を収集 投影元へのアクセスなど project/ • branch-a/ • License.txt • main/ • Readme.txt ProjFS マージ結果 -
32 ©SEGA • ProjFSは NTFS (NT File System)の機能を活用 – リパースポイント
(再解析ポイント) • シンボリックリンクやジャンクションなど – ファイルやフォルダを区別しないエントリ形式 ProjFSの仕組み (下位ファイルシステム側) ※ リパースポイントがキー要素なので、ReFSでも使用可能 -
33 ©SEGA • 一般的なファイルの格納状態 ProjFSの仕組み (下位ファイルシステム側) HEADER MFT $MFT $MFTMirr
... File File MFTエントリ情報 ファイルエントリ情報 Attr: $STANDARD_INFORMATION Attr: $FILE_NAME Attr: $DATA (Non-Resident) Cluster: File Data Cluster: File Data Cluster: File Data Data Run List Disk / Volume Volume内クラスタを示す ファイル 内容 -
34 ©SEGA • リパースポイントがある場合 (IO_REPARSE_TAG_PROJFS) ProjFSの仕組み (下位ファイルシステム側) HEADER MFT $MFT
$MFTMirr ... File File MFTエントリ情報 ファイルエントリ情報 Attr: $STANDARD_INFORMATION Attr: $FILE_NAME Attr: $REPARSE_POINT Disk / Volume Attr: $DATA (Non-Resident) Data Run List 実体化のタイミングで構成 Cluster: File Data Cluster: File Data Cluster: File Data ファイル 内容 Data Run List -
36 ©SEGA • ProjFSを使用したSlimSyncというツールを開発 – バージョン管理システム Perforce と接続 – 開発言語はC#を用いて実装
• ProjFS APIはWin32API • C#によるラッパーライブラリを使用 – GDC2022講演でも触れられていたMicrosoft製 – https://github.com/microsoft/ProjFS-Managed-API 設計事例 現在は”Helix Core” 講演内は馴染み深い Perforce で表記 -
37 ©SEGA • ツールはサーバー・クライアント型の実装 – ProjFSを制御するファイルシステムプロバイダ – ユーザー操作を担当するクライアント • リポジトリ(ディポ)との同期、状態の変更など
SlimSync SlimSync (ファイルシステム プロバイダ) SlimSync (クライアント) gRPC ProjFS -
38 ©SEGA • ローカルのリポジトリ操作に 専用ツール(SlimSyncクライアント)が必要 – バージョン管理ツールに備わる標準ツールでは ファイル自体が取得されてしまう – 同期操作によって、
「ファイルは既にある状態」に見せかけたい SlimSync p4.exe, git.exe, svn.exe,... -
39 ©SEGA Repository Backend SlimSync構造図 (ファイルシステムプロバイダ側) SlimSync Main [SYSTEM] File
Request Perforceアダプター Perforce (Depot) ProjFS Callback処理部 他リポジトリ用 アダプター Repository Web Server (Kestrel) Client Web Debug Console Debug gRPC 制御 gRPC -
42 ©SEGA 『ファンタシースターオンライン2』(=PSO2)とは 2012年7月にサービス開始されたセガのオンラインアクションRPG 「無限の冒険」「オンラインRPG最高峰のアクション」 「究極のキャラクタークリエイト」「境界を超えるRPG」 をコンセプトに長期運営(今年で12周年!) 2021年6月にグラフィックエンジンを一新し、大幅リニューアルしたのが、 『ファンタシースターオンライン2 ニュージェネシス』(=NGS)
超大型アップデートにより『PSO2』から1000年後の世界をオープンフィールドで実現。 『PSO2』の世界はそのままプレイ可能で、セーブデータも共有し 同じキャラクターで、『PSO2』と『NGS』の世界を自由に行き来できるのが最大の特徴。 【対応プラットフォーム】PC、PS4、Xbox One、Switch(クラウド) 『PSO2 ニュージェネシス』とは? -
43 ©SEGA • バージョン管理システム: Perforce(Helix Core) • ストレージ: HDD 6TB
SSD 2TB 『NGS』の環境情報 -
44 ©SEGA • 既に運用中のタイトル – 開発に影響がでる変化はNG • 仮想ファイルシステム(ProjFS)は 開発PCごとに使用の有無が設定可能 –
チーム全体への影響が極めて少ない – 開発PC上で動作するので、 Perforceサーバー側の変更不要 – JenkinsエージェントをSlimSyncに置き換えて検証 『NGS』で検証するにあたって -
45 ©SEGA • 1ブランチのデータ量 – 全体 ▪ 1.09TB – コード
▪ 約50,000ファイル – アセット ▪ 約1,200,000ファイル ▪ 約800GByte 『NGS』のブランチ戦略 -
46 ©SEGA • 2ヵ月の間隔でリリースのためブランチ • 最大で5ブランチ並行しての作業 1TB * 5ブランチ =
5TB 『NGS』のブランチ戦略 -
47 ©SEGA • 新規取得からビルド完了までの時間 • ビルド後に実体化されたファイルを調査 検証①アプリケーションのビルドへの効果 リポジトリ全体 投影されたファイル(メタデータのみが存在) 実体化されたファイル
(ファイル内容がストレージ上に存在) ローカルファイル 中間成果物等 SlimSync無効の場合のサイズ SlimSync有効の場合のサイズ -
48 ©SEGA • 新規取得から実行した場合 検証①アプリケーションのビルドへの効果 SlimSync 有無 リボジトリの同期 ビルド時間 合計時間
ストレージ使用量 無効 2:55:40 0:22:41 3:18:21 1067.2GiB 有効 0:20:48 1:01:55 1:22:43 1.18GiB -
49 ©SEGA • 差分取得を行ってから実行した場合 検証①アプリケーションのビルドへの効果 SlimSync 有無 リボジトリの同期 ビルド時間 合計時間
ストレージ使用量 無効 0:00:41 0:18:58 0:19:39 1067.2GiB 有効 0:00:12 0:20:53 0:21:05 1.18GiB -
50 ©SEGA • オンデマンドのため、時間の掛かり方が変化 リポジトリ同期のコストに対する補足 sync begin sync end Perforce
FILE1 転送 FILE1 保存 FILE2 転送 FILE2 保存 FILE3 転送 FILE3 保存 FILE2 open FILE2 read sync begin sync end slimsync FILE2 転送 FILE2 保存 FILE2 open FILE2 read リビジョン管理 リビジョン管理 メリット: sync end までが速い デメリット: open から read までが遅い(※) メリット: 転送量が少ない メリット: 占有量が少ない -
53 ©SEGA • ワークスペースマッピングの設定方法 o 1行につき1つのマッピングで構成 o フォルダ単位での除外やファイルの除外 o ディポシンタックスに一致するファイルの除外
検証②ストレージの使用量について //depot/... //work/... -//depot/main/... //work/main/... //depot/main/source/... //work/main/source/... //depot/main/data/... //work/main/data/... //depot/main/asset/....json //work/main/asset/....json ※実際に使用されている設定とは異なります。 -
54 ©SEGA • ワークスペースマッピングは用途に合わせて2種類用意 – パッケージビルド用 • パッケージ作成に必要なソースファイルとデータファイルのみを取得 – アセットビルド用
• アセットをデータに変換するためにアセットを全て取得 検証②ストレージの使用量について -
55 ©SEGA • パッケージビルドマシンのストレージ使用量 • ワークスペースマッピングでは完全に制限することが難しい – フォルダ単位で制限しているため必要としている フォルダに不要なファイルが含まれている 検証②ストレージの使用量について
SlimSync有無 使用量 ブランチ全体に 対する割合 無効 250GiB 23% 有効 129GiB 12% -
56 ©SEGA • アセットビルドマシンのストレージ使用量 • アセットの特徴 – 最新のブランチで使用するアセットは限られている • 『PSO2』のアセットで直近5ブランチで使用されたファイルは100MB未満
– バージョンごとに分かれておらず、新旧のデータが入り乱れている – 古いアセットも再利用や不具合修正のため必要になることがある 検証②ストレージの使用量について SlimSync有無 使用量 ブランチ全体に 対する割合 無効 1045.72GiB 98% 有効 167.3GiB 15% -
58 ©SEGA • 仮想化による効果がでるケース/でないケース – 仮想化による恩恵は、 「使用するファイルが分散している」 場合に大きくなる。 – 「ファイル全てを必要とする」
場合は効果が見込めない。 考察 仮想化による効果の度合い -
59 ©SEGA • 仮想による効果が大きいケース 考察 仮想化による効果の度合い アクセスする対象が別 (フォルダ/ファイル) 1つのフォルダ内に デザイナ用/プログラマ用が混在していてもOK
※フォルダの構成は プロジェクトのものと無関係です -
62 ©SEGA • 仮想化によって効率的になる部分があった – 作業始めの時間は明らかに短縮 – ストレージの実質的な使用量は削減 • 環境によっては効果が出せないケースも存在している
– 使用しているエンジンやツールとの相性 – 役割ごとに綺麗にファイルが分けられている場合 • ワークスペースマッピングによる制限が不要になる – 細かなマッピングが不要になり 設定する労力の削減や設定ミスにる不具合の防止が可能 検証まとめ -
65 ©SEGA Perforce によるエンコーディング変換の概略 事象: ファイル末尾が欠落する 知識 TXT Perforce リポジトリサーバー
データベース TXT クライアント ワークスペース クライアント設定 (エンコーディング指定) クライアント ファイルタイプと クライアント設定に応じた 文字コード・改行コード エンコーディング変換 (ファイルサイズ増大) ローカルストレージ リポジトリ/ディポ ファイルはすべて↓で保存 • 文字コード=UTF8 固定 • 改行コード=LF 固定 ファイルタイプ (エンコーディング指定) UTF8/LF SJIS/CRLF SlimSync ツール -
66 ©SEGA 原因は ファイルサイズの認識のズレ 対応策 • VCS 側のエンコーディング変換機能を回避 • ファイルオープン時に先行取得してサイズ確定
↓ 事象: ファイル末尾が欠落する 対策 Virtual Placeholder Hydrated open read Full write フォルダー列挙 ファイルオープン ファイルリード バイナリファイル時 p4 dirs (何もしない) p4 print テキストファイル時 p4 dirs p4 print p4 print -
68 ©SEGA IDisposable インターフェース 事象: ファイルの先頭が破損する 知識 using (FileStream fs
= File.Create("README")) { AddText(fs, "Hello."); } FileStream fs = File.Create("README"); AddText(fs, "Hello."); -
69 ©SEGA 原因は初歩的な リソースリーク 事象: ファイルの先頭が破損する 対策 using (var content
= _virtualizationInstance.CreateWriteBuffer(length)) { GetFileContent(...); hr = _virtualizationInstance.WriteFileData(...); } var content = _virtualizationInstance.CreateWriteBuffer(length); GetFileContent(...); hr = _virtualizationInstance.WriteFileData(...); 実体は IWriteBuffer で これは IDisposable -
71 ©SEGA ProjFS におけるテーブルマージのルール 事象: 大文字/小文字が正しくない 知識 ProjFS ドライバー NTFS
ファイルシステムドライバー エクスプローラー など ランタイムライブラリ ファイルシステムドライバー ストレージクラスドライバー HDD/SSD my-project/ • main/ • Readme.txt ストレージより my-project/ • MAIN/ • License.txt プロバイダより my-project/ • main/ or MAIN/? • License.txt • Readme.txt ProjFS マージ結果 微妙に違う… どうなる…? -
72 ©SEGA 原因は ”正しい”エントリー名の規定の不足 事象: 大文字/小文字が正しくない 対策 リポジトリサーバー SlimSync ツール
クライアント "D:¥my-project¥Main¥dev¥UEproject¥fpsgame" "D:¥my-project¥main¥dev¥ueproject¥FPSGame" リポジトリ情報を参考に パスを正規化する データベース -
73 ©SEGA 事象: OS が不安定になる 課題 ツール担当者 タイトル担当者 ツール完成! テストもパス!
SlimSync ツール リリース … … … -
75 ©SEGA 環境変数 PATH の解決処理 事象: OS が不安定になる 知識 20
個のパスが登録されていれば、1 回のローディングにつき 最悪 20 回の I/O アクセスが こっそり発生 -
76 ©SEGA 原因は 遅延ローディングとパス解決 によるデッドロック 対策案 • 遅延ローディングの徹底排除 • SlimSync
ツールからのワークスペース参照禁止 事象: OS が不安定になる 対策 DLL 起動 DLL DLL をサーチ 未ロード 仮想化開始! アクセス パス解決 遅延ロード DLL 未ロード のまま -
78 ©SEGA 独自のコンテンツキャッシュ仕様 • 初見のファイルはキャッシュフォルダーに溜め込む • ファイル名はリポジトリハッシュから一意に決定 今回実装したロジック 事象: 作業途中で進行停止する
知識 キャッシュの 存在確認 コンテンツ 取得 キャッシュ Read キャッシュ Read キャッシュ Write : キャッシュヒット時 : キャッシュミス時 -
79 ©SEGA 事象: 作業途中で進行停止する 知識 存在確認 取得 Read Write 1つめのスレッド:
(キャッシュミス) 存在確認 取得 Read Write 2つめのスレッド: (キャッシュミス) Read 存在確認 3つめのスレッド: (キャッシュヒット) オープンエラー/ オープン時にデッド ロック 書き込み先フォルダーに同名ファイルがある ↓ 暗黙の DeleteFile() が発生 -
80 ©SEGA 原因はマルチスレッド対応での 排他ロック不足 C# ではアノテーションが利用可能 事象: 作業途中で進行停止する 対策 キャッシュの
存在確認 コンテンツ 取得 キャッシュ Read キャッシュ Read キャッシュ Write GetFileContentCache(): 再入 NG [MethodImpl(MethodImplOptions.Synchronized)] public bool GetFileContentCache(...) { -
81 ©SEGA 事象: ファイルオープンに失敗する 課題 コンパイル 問題なし! SlimSync ツールに ツール自身からの
アクセスが到達してる… デッドロック回避処理による File Not Found だ… SlimSync ツールは 直接ファイルアクセスしない 設計なのに…??? -
82 ©SEGA パスを扱う “Path” クラス 事象: ファイルオープンに失敗する 知識 string path1
= @"mydir"; string path2 = @"¥mydir"; string fullPath; fullPath = Path.GetFullPath(path1); Console.WriteLine("GetFullPath() returns ‘{0}'", fullPath); // GetFullPath() returns 'C:¥temp¥Demo¥mydir’ fullPath = Path.GetFullPath(path2); Console.WriteLine("GetFullPath() returns ‘{0}'", fullPath); // GetFullPath() returns 'C:¥mydir' string path1 = "C:/Program Files/Common Files"; string path2 = "C:/Program Files/Microsoft"; string relPath; relPath = Path.GetRelativePath(path1, path2); Console.WriteLine("GetRelativePath() returns ‘{0}’”, relPath); // GetRelativePath() returns ‘../Microsoft’ Path.GetFullPath() Path.GetRelativePath() -
83 ©SEGA Windows ファイルシステムの古い仕様(8.3 形式) 事象: ファイルオープンに失敗する 知識 Path クラスは
短縮ファイル名にも対応している 一部メソッドは 短縮ファイル名を展開する その際、ディレクトリエントリーを読み出す 短縮ファイル名を 持つエントリー -
84 ©SEGA 原因は Path クラスによる 短縮ファイル名の展開 の試行 対応策 • ~
記号を含まないルールでのリポジトリ運用 • Path クラスのクローン実装による代替(特に下記) GetFullPath() GetRelativePath() 事象: ファイルオープンに失敗する 対策 -
85 ©SEGA • 使い方の不備があると Windows に波及することも • "ProjFS を使うこと" =
"OS をハックすること" → 地道で綿密な作業の連続 TIPS まとめ -
87 ©SEGA • ProjFS と VCS の相性はとても良い • 実装の難易度はやや高め(しかし 取り組む価値あり)
• ProjFS の知名度は低い(しかし とても興味深い技術) まとめ