-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Description
Edit by carlossanlop: API Proposal can be found here.
Description
The FileStream class doesn't currently support Windows file preallocation hints reducing the performance of file operations while also increasing the disk fragmentation of newly created files.
If we use the example for file uploads for aspnetcore here:
https://docs.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-5.0#file-upload-scenarios
using (var stream = System.IO.File.Create(filePath))
{
await formFile.CopyToAsync(stream);
}
None of the .NET classes support passing the AllocationSize when creating the file even though it's been a feature included with Windows since 2000. When you're creating multiple files with large file sizes such as with servers and file uploads, installers or build servers (we use .netcore for our builds servers compiling native C) passing the file length as AllocationSize when creating the file can significantly reduce fragmentation and improve performance.
An example of passing the allocation size:
using (var stream = System.IO.File.Create(filePath, allocationSize: 1073741824))
{
await formFile.CopyToAsync(stream);
}
Windows Vista and above support the creation of files with their initial AllocationSize. This hint is passed to the file system driver and we can use the published FAT driver source code to show these optimizations:
https://github.com/microsoft/Windows-driver-samples/blob/master/filesys/fastfat/allocsup.c#L1164
https://github.com/microsoft/Windows-driver-samples/blob/master/filesys/fastfat/allocsup.c#L1233-L1250
The FatTruncateFileAllocation procedure becomes a noop when the AllocationSize is valid:
https://github.com/microsoft/Windows-driver-samples/blob/master/filesys/fastfat/allocsup.c#L1533
You can search for AllocationSize and see the other optimizations when the value is known during file creation:
https://github.com/microsoft/Windows-driver-samples/blob/6c1981b8504329521343ad00f32daa847fa6083a/filesys/fastfat/create.c#L6493-L6506
It would be safe to assume similar optimisations in the NTFS and ReFS drivers when AllocationSize is valid.
Configuration
The AllocationSize must be passed with NtCreateFile when a file is being created, overwritten, or superseded. NtCreateFile is documented here: https://docs.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntcreatefile
NTSTATUS
NTAPI
NtCreateFile(
_Out_ PHANDLE FileHandle,
_In_ ACCESS_MASK DesiredAccess,
_In_ POBJECT_ATTRIBUTES ObjectAttributes,
_Out_ PIO_STATUS_BLOCK IoStatusBlock,
_In_opt_ PLARGE_INTEGER AllocationSize, // <---- File size
_In_ ULONG FileAttributes,
_In_ ULONG ShareAccess,
_In_ ULONG CreateDisposition,
_In_ ULONG CreateOptions,
_In_reads_bytes_opt_(EaLength) PVOID EaBuffer,
_In_ ULONG EaLength
);
FileStream should also support get/set for the file allocation size like it currently does for the file size using GetFileInformationByHandleEx with the FileAllocationInfo class and FILE_ALLOCATION_INFO however this doesn't receive the full benefits of passing the AllocationSize up front with NtCreateFile so both methods should be supported for different use cases.
Analysis
A reduction in IO for our workloads when using C# to create files:

Data
There's also a discussion on stackoverflow about preallocation with more details:
https://stackoverflow.com/questions/53334343/windows-refs-ntfs-file-preallocation-hint
.NET including support for preallocation hints would be very welcome feature for reducing disk fragmentation on servers (file uploads) and desktop applications (installers and build tools). Please consider adding support for this feature into the next version of the runtime.