Comment créer des écritures de stream de fichiers rapides et efficaces sur de gros fichiers fragmentés

J’ai une application qui écrit des fichiers volumineux dans plusieurs segments. J’utilise FileStream.Seek pour positionner chaque wirte. Il semble que lorsque j’appelle FileStream.Write à un emplacement profond dans un fichier fragmenté, l’écriture déclenche une opération de “renvoi” (écriture de 0) sur tous les octets précédents, ce qui est lent.

Existe-t-il un moyen plus efficace de gérer cette situation?

Le code ci-dessous illustre le problème. L’écriture initiale prend environ 370 MS sur ma machine.

public void WriteToStream() { DateTime dt; using (FileStream fs = File.Create("C:\\testfile.file")) { fs.SetLength(1024 * 1024 * 100); fs.Seek(-1, SeekOrigin.End); dt = DateTime.Now; fs.WriteByte(255); } Console.WriteLine(@"WRITE MS: " + DateTime.Now.Subtract(dt).TotalMilliseconds.ToSsortingng()); } 

NTFS prend en charge les fichiers fragmentés , mais il n’existe aucun moyen de le faire dans .net sans invoquer certaines méthodes natives.

Il n’est pas très difficile de marquer un fichier comme étant fragmenté, sachez qu’une fois qu’un fichier est marqué comme fichier fragmenté, il ne peut jamais être reconverti en fichier non fragmenté, sauf en copiant le fichier entier dans un nouveau fichier non fragmenté.

Exemple d’utilisation

 class Program { [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] private static extern bool DeviceIoControl( SafeFileHandle hDevice, int dwIoControlCode, IntPtr InBuffer, int nInBufferSize, IntPtr OutBuffer, int nOutBufferSize, ref int pBytesReturned, [In] ref NativeOverlapped lpOverlapped ); static void MarkAsSparseFile(SafeFileHandle fileHandle) { int bytesReturned = 0; NativeOverlapped lpOverlapped = new NativeOverlapped(); bool result = DeviceIoControl( fileHandle, 590020, //FSCTL_SET_SPARSE, IntPtr.Zero, 0, IntPtr.Zero, 0, ref bytesReturned, ref lpOverlapped); if(result == false) throw new Win32Exception(); } static void Main() { //Use stopwatch when benchmarking, not DateTime Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); using (FileStream fs = File.Create(@"e:\Test\test.dat")) { MarkAsSparseFile(fs.SafeFileHandle); fs.SetLength(1024 * 1024 * 100); fs.Seek(-1, SeekOrigin.End); fs.WriteByte(255); } stopwatch.Stop(); //Returns 2 for sparse files and 1127 for non sparse Console.WriteLine(@"WRITE MS: " + stopwatch.ElapsedMilliseconds); } } 

Une fois qu’un fichier a été marqué comme étant clairsemé, il se comporte maintenant comme vous l’avez excepté, il se comporte également dans les commentaires. Il n’est pas nécessaire d’écrire un octet pour marquer un fichier à une taille définie.

 static void Main() { ssortingng filename = @"e:\Test\test.dat"; using (FileStream fs = new FileStream(filename, FileMode.Create)) { MarkAsSparseFile(fs.SafeFileHandle); fs.SetLength(1024 * 1024 * 25); } } 

entrez la description de l'image ici

Voici du code pour utiliser des fichiers fragmentés:

 using System; using System.ComponentModel; using System.IO; using System.Runtime.InteropServices; using System.Text; using System.Threading; using Microsoft.Win32.SafeHandles; public static class SparseFiles { private const int FILE_SUPPORTS_SPARSE_FILES = 64; private const int FSCTL_SET_SPARSE = 0x000900c4; private const int FSCTL_SET_ZERO_DATA = 0x000980c8; public static void MakeSparse(this FileStream fileStream) { var bytesReturned = 0; var lpOverlapped = new NativeOverlapped(); var result = DeviceIoControl( fileStream.SafeFileHandle, FSCTL_SET_SPARSE, IntPtr.Zero, 0, IntPtr.Zero, 0, ref bytesReturned, ref lpOverlapped); if (!result) { throw new Win32Exception(); } } public static void SetSparseRange(this FileStream fileStream, long fileOffset, long length) { var fzd = new FILE_ZERO_DATA_INFORMATION(); fzd.FileOffset = fileOffset; fzd.BeyondFinalZero = fileOffset + length; var lpOverlapped = new NativeOverlapped(); var dwTemp = 0; var result = DeviceIoControl( fileStream.SafeFileHandle, FSCTL_SET_ZERO_DATA, ref fzd, Marshal.SizeOf(typeof(FILE_ZERO_DATA_INFORMATION)), IntPtr.Zero, 0, ref dwTemp, ref lpOverlapped); if (!result) { throw new Win32Exception(); } } public static bool SupportedOnVolume(ssortingng filename) { var targetVolume = Path.GetPathRoot(filename); var fileSystemName = new SsortingngBuilder(300); var volumeName = new SsortingngBuilder(300); uint lpFileSystemFlags; uint lpVolumeSerialNumber; uint lpMaxComponentLength; var result = GetVolumeInformationW( targetVolume, volumeName, (uint)volumeName.Capacity, out lpVolumeSerialNumber, out lpMaxComponentLength, out lpFileSystemFlags, fileSystemName, (uint)fileSystemName.Capacity); if (!result) { throw new Win32Exception(); } return (lpFileSystemFlags & FILE_SUPPORTS_SPARSE_FILES) == FILE_SUPPORTS_SPARSE_FILES; } [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool DeviceIoControl( SafeFileHandle hDevice, int dwIoControlCode, IntPtr InBuffer, int nInBufferSize, IntPtr OutBuffer, int nOutBufferSize, ref int pBytesReturned, [In] ref NativeOverlapped lpOverlapped); [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool DeviceIoControl( SafeFileHandle hDevice, int dwIoControlCode, ref FILE_ZERO_DATA_INFORMATION InBuffer, int nInBufferSize, IntPtr OutBuffer, int nOutBufferSize, ref int pBytesReturned, [In] ref NativeOverlapped lpOverlapped); [DllImport("kernel32.dll", EntryPoint = "GetVolumeInformationW")] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool GetVolumeInformationW( [In] [MarshalAs(UnmanagedType.LPWStr)] ssortingng lpRootPathName, [Out] [MarshalAs(UnmanagedType.LPWStr)] SsortingngBuilder lpVolumeNameBuffer, uint nVolumeNameSize, out uint lpVolumeSerialNumber, out uint lpMaximumComponentLength, out uint lpFileSystemFlags, [Out] [MarshalAs(UnmanagedType.LPWStr)] SsortingngBuilder lpFileSystemNameBuffer, uint nFileSystemNameSize); [StructLayout(LayoutKind.Sequential)] private struct FILE_ZERO_DATA_INFORMATION { public long FileOffset; public long BeyondFinalZero; } } 

Et exemple de code pour tester la classe ci-dessus.

 class Program { static void Main(ssortingng[] args) { using (var fileStream = new FileStream("test", FileMode.Create, FileAccess.ReadWrite, FileShare.None)) { fileStream.SetLength(1024 * 1024 * 128); fileStream.MakeSparse(); fileStream.SetSparseRange(0, fileStream.Length); } } } 

J’espère que cela t’aides