kkamegawa's weblog

Visual Studio,TFS,ALM,VSTS,DevOps関係のことについていろいろと書いていきます。Google Analyticsで解析を行っています

Windows 7で高速にファイルを列挙させるFindFirstFileEx

長い間、NTFSチューニングの一つに「短いファイル名を生成させない」という技がありました。Windows Server 2008 R2のベストプラクティスアナライザーでも遅いディスクに対してはこのアドバイスが下されます。
NTFS Performance with Numerous Long Filenames
とはいっても、短いファイル名の生成をやめると、どうしても互換性の問題が発生します。短いファイル名を意図的に使っている場合もあります。空白ファイル名使うときめんどくさいですしね…。じゃあ、OSのほうでなんとかすればいいじゃないってことで、遅まきながらWindows 7でフラグが追加されることになりました。FindExInfoBasicを指定すれば、短いファイル名をWIN32_FIND_DATA構造体に格納せず、少し早くなると書かれています。個人的にはVistaか、XPで追加してくれてもよかったと思わないでもないです…。
FindFirstFileEx function (Windows)
FINDEX_INFO_LEVELS enumeration (Windows)

#define WINVER 0x601
#include <windows.h>
void FindNextTest(HANDLE hFile)
{
	WIN32_FIND_DATA FindFileData;
	BOOL fRet;
	do {
		fRet = FindNextFile(hFile, &FindFileData);
	}while(fRet);
}

/*
  Windows Vistaまで
 */
void FindFirstTest1(LPCTSTR lpszName)
{
         WIN32_FIND_DATA FindFileData;
	HANDLE hFile = FindFirstFileEx(lpszName, FindExInfoStandard , &FindFileData, 
		FindExSearchNameMatch, NULL, 0);

	if(hFile != INVALID_HANDLE_VALUE) {
		FindNextTest(hFile);
		FindClose(hFile);
	}
}
/*
  Windows 7の拡張を使ったとき
 */
void FindFirstTest2(LPCTSTR lpszName)
{
	WIN32_FIND_DATA FindFileData;
	HANDLE hFile = FindFirstFileEx(lpszName, FindExInfoBasic, &FindFileData, 
		FindExSearchNameMatch, NULL, 0);

	if(hFile != INVALID_HANDLE_VALUE) {
		FindNextTest(hFile);
		FindClose(hFile);
	}
}

Windows Server 2008 R2(2010/9/18時点のパッチ適用済み)において、このフラグ指定して11万弱のファイルをすべて列挙させると、FindFirstTest2()では手元の環境で81%くらいの時間になりました。あ、キャッシュなどに入らないように、実験ごとにOSを再起動しています。入っていると、1秒とか3秒で終わります。
Windows 7以降のOSで1フォルダ内に大量のファイルがあるならFindFirstFile()ではなく、FindFirstFileEx()でこのフラグ使いたいですね。
といっても、ふつうは.NET 4で強化されたDirectoryInfo.EnumerateFiles Method (String, SearchOption) (System.IO)あたりを使っていたほうが幸せだと思います…。