假设我有多个进程在写入大文件(20GB+)。每个进程都在写入自己的文件,假设该进程一次写入x MB,然后进行一些处理并再次写入x MB等等。
由于文件块在磁盘上连续分配,这种写入模式会导致文件严重碎片化。
当然,通过在打开文件时使用SetEndOfFile来“预分配”文件,然后在关闭之前设置正确的大小,可以轻松解决此问题。但是,现在访问这些文件的远程应用程序可以解析这些正在进行中的文件,显然在文件末尾看到零,并需要更长时间来解析文件。我无法控制这个读取应用程序,因此无法优化它以考虑末尾的零。
另一个肮脏的解决方法是更频繁地运行碎片整理,运行Systernal的contig实用程序或甚至实现自定义“碎片整理器”,该碎片整理器将处理我的文件并将它们的块合并在一起。
另一个更激进的解决方案是实现一个minifilter驱动程序,该驱动程序将报告“虚假”的文件大小。
当应用程序执行并在SetEndOfFile后等待按键时,我检查了磁盘上的NTFS结构:
该图表明NTFS已为我的文件分配了簇。然而,未命名的DATA属性将StreamDataSize指定为0。
Systernals DiskView也确认已分配簇
按Enter键以允许测试继续(并且需要相当长的时间,因为文件是在缓慢的USB驱动器上创建的),则StreamDataSize字段将被更新
自从我在末尾写了1个字节,NTFS现在确实必须将所有内容都清零,因此
我非常感谢回答/评论也提供官方参考资料来支持所作出的声明。
哦,测试应用程序在我的情况下输出以下内容:
此外,为了完整起见,以下是设置
由于文件块在磁盘上连续分配,这种写入模式会导致文件严重碎片化。
当然,通过在打开文件时使用SetEndOfFile来“预分配”文件,然后在关闭之前设置正确的大小,可以轻松解决此问题。但是,现在访问这些文件的远程应用程序可以解析这些正在进行中的文件,显然在文件末尾看到零,并需要更长时间来解析文件。我无法控制这个读取应用程序,因此无法优化它以考虑末尾的零。
另一个肮脏的解决方法是更频繁地运行碎片整理,运行Systernal的contig实用程序或甚至实现自定义“碎片整理器”,该碎片整理器将处理我的文件并将它们的块合并在一起。
另一个更激进的解决方案是实现一个minifilter驱动程序,该驱动程序将报告“虚假”的文件大小。
显然,上面列出的两种解决方案都远非最佳选择。因此,我想知道是否有一种方法可以向文件系统提供文件大小的提示,以便它在驱动器上“保留”连续的空间,但仍向应用程序报告正确的文件大小?
否则,每次写入更大的块显然都有助于减少碎片,但仍然无法解决问题。
编辑:
由于在我的情况下 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结构:
![before](https://istack.dev59.com/OjF3H.webp)
Systernals DiskView也确认已分配簇
![DickView](https://istack.dev59.com/Jz99Y.webp)
![enter image description here](https://istack.dev59.com/mPj5P.webp)
SetEndOfFile
确实有助于解决我所担心的问题。我非常感谢回答/评论也提供官方参考资料来支持所作出的声明。
哦,测试应用程序在我的情况下输出以下内容:
creating file
file extended, elapsed: 0
writing 'A' at the end
written: 1 bytes, elapsed: 21735
此外,为了完整起见,以下是设置
FileAllocationInfo
时DATA属性的示例(请注意,我为此图片创建了一个新文件)
![enter image description here](https://istack.dev59.com/5HbRw.webp)
SetEndOfFile()
确实可以减少并通常可以防止碎片化。我们在为我们的一个产品实现并行下载以抵消RTT时想知道它是否有帮助,结果发现确实有帮助。找到这个真是太好了。@HansPassant 这就是预期的效果。如果它会立即在阻塞操作中分配,我们将不必要地使应用程序停滞。它只是向文件系统API提供了一个提示,它恰好采取了正确的方式:“如果可能,请保留这么多未碎片化的空间以扩展文件”。 - Zyl