我通过查看 NTFS MFT / USN 日志来枚举 NTFS 硬盘分区的文件:
HANDLE hDrive = CreateFile(szVolumePath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, NULL, NULL);
DWORD cb = 0;
MFT_ENUM_DATA med = { 0 };
med.StartFileReferenceNumber = 0;
med.LowUsn = 0;
med.HighUsn = MAXLONGLONG; // no change in perf if I use med.HighUsn = ujd.NextUsn; where "USN_JOURNAL_DATA ujd" is loaded before
unsigned char pData[sizeof(DWORDLONG) + 0x10000] = { 0 }; // 64 kB
while (DeviceIoControl(hDrive, FSCTL_ENUM_USN_DATA, &med, sizeof(med), pData, sizeof(pData), &cb, NULL))
{
med.StartFileReferenceNumber = *((DWORDLONG*) pData); // pData contains FRN for next FSCTL_ENUM_USN_DATA
// here normaly we should do: PUSN_RECORD pRecord = (PUSN_RECORD) (pData + sizeof(DWORDLONG));
// and a second loop to extract the actual filenames
// but I removed this because the real performance bottleneck
// is DeviceIoControl(m_hDrive, FSCTL_ENUM_USN_DATA, ...)
}
FindFirstFile
它有效,它比通常的枚举技术快得多。但我认为它还不是最优的:
在我的 700k 文件
C:\
中,需要 21 秒。(此措施必须在重启后进行,否则会因为缓存不正确)。我见过另一个索引软件(不是 Everything,另一个)能够
C:\
在 < 5 秒内(在 Windows 启动后测量)进行索引,而无需读取 .db 文件中预先计算的数据库(或其他可以加快速度的类似技巧! )。本软件不使用FSCTL_ENUM_USN_DATA
,而是使用低级 NTFS 解析。
我试图提高性能的方法:
使用另一个标志打开文件,例如
FILE_FLAG_SEQUENTIAL_SCAN
,FILE_FLAG_RANDOM_ACCESS
或FILE_FLAG_NO_BUFFERING
: 相同的结果: 21 秒读取查看Estimate the number of USN records on NTFS volume,为什么在 VB.NET 中使用 DeviceIoControl 进行文件枚举比在 C++ 中更快? 我已经深入研究过它们,但它并没有为这个实际问题提供答案。
测试另一个编译器:MinGW64 而不是 VC++ Express 2013:相同的性能结果,没有区别
在 VC++ 上,我已经切换到
Release
而不是Debug
:是否有其他项目属性/选项可以加快程序的速度?
问题:
是否有可能提高性能DeviceIoControl(hDrive, FSCTL_ENUM_USN_DATA, ...)
?
或者是提高性能的唯一方法是对 NTFS 进行低级手动解析?
注意:根据测试,DeviceIoControl(hDrive, FSCTL_ENUM_USN_DATA, ...)
我的 700k 文件在这些过程中要读取的总大小仅为84MB。读取 84MB 的 21 秒仅为 4 MB/秒(我确实有 SSD!)。可能还有一些性能提升的空间,你不这么认为吗?