假设我有多个进程写入大文件(20gb+)。每个进程都在写入自己的文件,并假设该进程一次写入 x mb,然后进行一些处理并再次写入 x mb,等等。
发生的情况是这种写入模式导致文件严重碎片化,因为文件块在磁盘上连续分配。
当然,通过SetEndOfFile
在打开文件时使用“预分配”文件,然后在关闭文件之前设置正确的大小,很容易解决这个问题。但是现在远程访问这些文件的应用程序能够解析这些正在进行的文件,显然会在文件末尾看到零,并且解析文件需要更长的时间。我无法控制这个阅读应用程序,所以我无法优化它以在最后考虑零。
另一个肮脏的解决方法是更频繁地运行碎片整理,运行 Systernal 的 contig 实用程序,甚至实现一个自定义的“碎片整理程序”,它将处理我的文件并将它们的块合并在一起。
另一个更激进的解决方案是实现一个微型过滤器驱动程序,它会报告一个“假”文件大小。
但显然上面列出的两种解决方案都远非最佳。所以我想知道是否有办法向文件系统提供文件大小提示,以便它“保留”驱动器上的连续空间,但仍向应用程序报告正确的文件大小?
否则显然一次写入更大的块显然有助于碎片化,但仍然不能解决问题。
编辑:
由于SetEndOfFile
在我的情况下的有用性似乎存在争议,我做了一个小测试:
LARGE_INTEGER size;
LARGE_INTEGER a;
char buf='A';
DWORD written=0;
DWORD tstart;
std::cout << "creating file\n";
tstart = GetTickCount();
HANDLE f = CreateFileA("e:\\test.dat", GENERIC_ALL, FILE_SHARE_READ, NULL, CREATE_ALWAYS, 0, NULL);
size.QuadPart = 100000000LL;
SetFilePointerEx(f, size, &a, FILE_BEGIN);
SetEndOfFile(f);
printf("file extended, elapsed: %d\n",GetTickCount()-tstart);
getchar();
printf("writing 'A' at the end\n");
tstart = GetTickCount();
SetFilePointer(f, -1, NULL, FILE_END);
WriteFile(f, &buf,1,&written,NULL);
printf("written: %d bytes, elapsed: %d\n",written,GetTickCount()-tstart);
当应用程序执行并在 SetEndOfFile 之后等待按键时,我检查了磁盘上的 NTFS 结构:
该图像显示 NTFS 确实为我的文件分配了集群。但是,未命名的 DATA 属性已StreamDataSize
指定为 0。
当按下回车键以允许测试继续时(并等待相当长的时间,因为文件是在慢速 USB 记忆棒上创建的),该StreamDataSize
字段已更新
由于我最后写了 1 个字节,NTFS 现在真的必须将所有内容归零,所以SetEndOfFile
确实有助于解决我“烦恼”的问题。
我非常感谢答案/评论也提供了官方参考来支持所提出的主张。
哦,在我的情况下,测试应用程序会输出这个:
creating file
file extended, elapsed: 0
writing 'A' at the end
written: 1 bytes, elapsed: 21735
同样为了完整起见,这里是一个示例,设置时 DATA 属性的外观FileAllocationInfo
(请注意,我为此图片创建了一个新文件)