2017-01-05 2 views
2

In einem Tool, das große Dateien zwischen den Festplatten kopiert, ersetzte ich die System.IO.FileInfo.CopyTo-Methode durch System.IO.Stream.CopyToAsync. Dies ermöglicht eine schnellere Kopie und eine bessere Kontrolle während der Kopie, z. Ich kann die Kopie stoppen. Aber das erzeugt noch mehr Fragmentierung der kopierten Dateien. Es ist besonders ärgerlich, wenn ich Dateien von mehreren hundert Megabyte kopiere.C# I/O async (copyAsync): Wie kann Dateifragmentierung vermieden werden?

Wie kann ich Datenträgerfragmentierung während der Kopie vermeiden?

Mit dem Befehl xcopy kopiert die Option/j Dateien ohne Pufferung. Und es wird in für sehr große Datei empfohlen TechNet Es scheint in der Tat Dateifragmentierung zu vermeiden (während einer einfache Kopieren von Dateien in Windows 10 Explorer meine Datei nicht fragmentieren!)

Eine Kopie ohne Pufferung die entgegengesetzte Richtung als dies zu sein scheint asynchrone Kopie. Oder gibt es eine Möglichkeit, Async-Kopie ohne Pufferung zu tun?

Hier ist es mein derzeitiger Code für eine sync Kopie. Ich lasse die Standardpuffergröße von 81920 Bytes, d.h. 10 * 1024 * Größe (int64).

Ich arbeite mit NTFS-Dateisystemen, also 4096 Bytes Cluster.

EDIT: ich den Code mit SetLength aktualisiert, wie vorgeschlagen, fügte der Fileoptions Async während die destinationStream Erstellen und fixieren Sie die Attribute nach dem Einstellen der Zeiteinstellung

(sonst Ausnahme für Readonly-Dateien ausgelöst wird)
 int bufferSize = 81920; 
     try 
     { 
      using (FileStream sourceStream = source.OpenRead()) 
      { 
       // Remove existing file first 
       if (File.Exists(destinationFullPath)) 
        File.Delete(destinationFullPath); 

       using (FileStream destinationStream = File.Create(destinationFullPath, bufferSize, FileOptions.Asynchronous)) 
       { 
        try 
        {        
         destinationStream.SetLength(sourceStream.Length); // avoid file fragmentation! 
         await sourceStream.CopyToAsync(destinationStream, bufferSize, cancellationToken); 
        } 
        catch (OperationCanceledException) 
        { 
         operationCanceled = true; 
        } 
       } // properly disposed after the catch 
      } 
     } 
     catch (IOException e) 
     { 
      actionOnException(e, "error copying " + source.FullName); 
     } 

     if (operationCanceled) 
     { 
      // Remove the partially written file 
      if (File.Exists(destinationFullPath)) 
       File.Delete(destinationFullPath); 
     } 
     else 
     { 
      // Copy meta data (attributes and time) from source once the copy is finished 
      File.SetCreationTimeUtc(destinationFullPath, source.CreationTimeUtc); 
      File.SetLastWriteTimeUtc(destinationFullPath, source.LastWriteTimeUtc); 
      File.SetAttributes(destinationFullPath, source.Attributes); // after set time if ReadOnly! 
     } 

I Fürchte auch, dass die File.SetAttributes und die Zeit am Ende meines Codes die Dateifragmentierung erhöhen könnten.

Gibt es einen richtigen Weg, um eine 1: 1 asynchrone Dateikopie ohne Dateifragmentierung zu erstellen, d. H. Die Festplatte fragt, ob die Datei nur zusammenhängende Sektoren erhält?

Andere Themen in Bezug auf Dateifragmentierung wie How can I limit file fragmentation while working with .NET schlägt vor, die Dateigröße in größeren Blöcken zu erhöhen, aber es scheint keine direkte Antwort auf meine Frage zu sein.

+0

Haben Sie versucht 'destinationStream.Length = sourceStream.Length' kurz vor dem Kopieren? –

+0

Gute Idee, Länge ist nur ein Getter, aber die SetLength-Methode macht den Job. Es scheint in der Tat Fragmentierung bei einem schnellen Test zu vermeiden! Ich sehe auch die FileOptions beim Erstellen des DestinationStream. Frage mich, ob Asynchronous oder WriteThrough eine gute Option sein könnte – EricBDev

Antwort

-1

Anbetracht Antwort Hans Passant mehr fragen, in meinem obigen Code, eine Alternative zu

destinationStream.SetLength(sourceStream.Length); 

wäre, wenn ich es verstanden richtig:

byte[] writeOneZero = {0}; 
destinationStream.Seek(sourceStream.Length - 1, SeekOrigin.Begin); 
destinationStream.Write(writeOneZero, 0, 1); 
destinationStream.Seek(0, SeekOrigin.Begin); 

Es scheint in der Tat die Kopie zu konsolidieren.

Aber ein Blick auf den Quellcode FileStream.SetLengthCore scheint es tut fast das gleiche, am Ende der Suche nach, aber ohne ein Byte schreiben:

private void SetLengthCore(long value) 
    { 
     Contract.Assert(value >= 0, "value >= 0"); 
     long origPos = _pos; 

     if (_exposedHandle) 
      VerifyOSHandlePosition(); 
     if (_pos != value) 
      SeekCore(value, SeekOrigin.Begin); 
     if (!Win32Native.SetEndOfFile(_handle)) { 
      int hr = Marshal.GetLastWin32Error(); 
      if (hr==__Error.ERROR_INVALID_PARAMETER) 
       throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_FileLengthTooBig")); 
      __Error.WinIOError(hr, String.Empty); 
     } 
     // Return file pointer to where it was before setting length 
     if (origPos != value) { 
      if (origPos < value) 
       SeekCore(origPos, SeekOrigin.Begin); 
      else 
       SeekCore(0, SeekOrigin.End); 
     } 
    } 

Wie auch immer, nicht sicher, dass Thesen Methode keine Fragmentierung garantieren, aber bei Vermeide es in den meisten Fällen. Somit wird das Auto Defragment Tool den Job mit einem geringen Leistungsaufwand beenden. Mein ursprünglicher Code ohne diese Suchaufrufe erzeugte Hunderttausende von Fragmenten für eine 1-GB-Datei, was meinen Computer verlangsamte, wenn das Defragmentierungstool aktiv wurde.

+0

Ich habe gestern eine 100-GB-VM-Datei kopiert, in der das Ziellaufwerk über genügend Speicherplatz verfügt (Ziel ist jedoch eine SSD, bei der die Fragmentierung nicht relevant ist und daher die Ergebnisse im Windows-Kernel ändern kann). a) mit Fenstern 10 Explorer/Kopieren: gleich 3 Fragmente c) mit dem Code, oben/writeOneZero/Such + write:: Zieldatei mit SetLength() 3 Fragmente b) hatte nur 1 Stück Somit dieser Such + Schreiben macht Sinn! – EricBDev

3

Ich denke, FileStream.SetLength ist, was Sie brauchen.

+1

Ich kam auch zu dieser Lösung mit Lucas Kommentar. Es reduziert die Fragmentierung sehr. Allerdings nicht ganz, ich habe noch ein paar Dateien nach der Kopie fragmentiert. Keine große Sache im Vergleich zum vorherigen Zustand, aber frage mich, ob ich noch besser machen könnte. Können wir keine Fragmentierung garantieren? – EricBDev

+1

Sie können das nur garantieren, wenn Sie den Datenträger vor jedem Kopiervorgang formatieren. –

+0

@HenkHolterman Sie haben Recht, aber auf der anderen Seite ist es möglich, die Fragmentierung im Falle mehrerer paralleler Schreibvorgänge –

2

aber die SetLength Methode macht den Job

Es ist nicht die Arbeit machen. Es nur aktualisiert die Dateigröße im Verzeichniseintrag, es weist keine Cluster zu. Der einfachste Weg, dies selbst zu sehen, ist dies in einer sehr großen Datei, sagen wir 100 Gigabyte. Beachten Sie, wie der Anruf sofort beendet. Nur wenn das Dateisystem nicht auch die Cluster zuweist und schreibt, kann es sofort sein. Das Lesen von der Datei ist tatsächlich möglich, obwohl die Datei keine tatsächlichen Daten enthält, gibt das Dateisystem einfach binäre Nullen zurück.

Dies führt auch zu irreführendem Dienstprogramm, das die Fragmentierung meldet. Da die Datei keine Cluster enthält, kann keine Fragmentierung auftreten. Es sieht also nur so aus, als hättest du dein Problem gelöst.

Das einzige, was Sie tun können, um die Zuordnung der Cluster zu erzwingen, besteht darin, tatsächlich in die Datei zu schreiben. Es ist tatsächlich möglich, 100 Gigabyte Cluster mit einem einzigen Schreibvorgang zuzuteilen. Sie müssen Seek() verwenden, um zu Length-1 zu positionieren, und dann ein einzelnes Byte mit Write() schreiben.Dies wird bei einer sehr großen Datei eine Weile dauern, es ist in Wirklichkeit nicht mehr asynchron.

Die Chancen, dass es Fragmentierung verringern wird, sind nicht groß. Sie haben lediglich das Risiko etwas reduziert, dass die Schreibvorgänge durch Schreibvorgänge von anderen Prozessen verschachtelt werden. Etwas, tatsächliches Schreiben wird durch den Dateisystemcache träge gemacht. Kernproblem ist, dass das Volume fragmentiert wurde, bevor Sie mit dem Schreiben begonnen haben. Es wird nie weniger fragmentiert sein, wenn Sie fertig sind.

Beste Sache zu tun ist, nur nicht darüber zu sorgen. Das Defragmentieren ist heutzutage unter Windows automatisch, seit Vista. Vielleicht möchten Sie play with the scheduling, vielleicht möchten Sie bei superuser.com

+0

"Dies führt auch dazu, dass ein Dienstprogramm, das die Fragmentierung meldet, in die Irre geführt wird. Da die Datei keine Cluster hat, kann es keine Fragmentierung geben " Aber die Datei wird schließlich geschrieben.Habe gerade einen Test mit einer 4-GB-Datei durchgeführt, die 16.000 Cluster belegt: In der Clusteransicht des Defrag Tools scheint alles zusammenhängend zu sein. – EricBDev

+0

Bitte sehen Sie meine entsprechende Antwort es ist was du meintest. Wie geschrieben, scheint es so unmittelbar wie SetLengh() und scheint keine Leistungseinbußen zu verursachen. Aber es garantiert auch nicht, dass alle Cluster zusammenhängend werden. Ich habe gerade getestet, um eine 60-GB-Datei auf einer Partition mit nur 90 GB zu kopieren. Die 60 GB wurden kopiert, aber in 3 Fragmenten, da meine Platte keine freien zusammenhängenden 60 GB hatte! (einige Cluster in der Mitte besetzt) ​​ – EricBDev

+0

wie in meiner Antwort oben kommentiert, die Suche + Schreibstrategie besser als SETLength mit einer 100 GB VM kopiert: ein Stück mit Such + Schreiben, während 3 Fragmente mit SetLength()! – EricBDev

Verwandte Themen