C# File.Copy() geschwindigkeit?

Es gibt mehrere Möglichkeiten:

1.) Du willst am Schluss wissen, wie schnell es war, dann liest du einfach vorher die Länge aus mit new FileInfo(Path).Length und dividierst es durch die vergangene Zeit (vorher und nachher mit DateTime.Now die Zeit auslesen und dann mit Ende.Ticks-Start.Ticks die Zeit ermitteln

2.) Du willst das Ganze laufend haben. Dann machst du zwei FileStreams (einen zum Lesen, einen zum Schreiben mit einer passenden Buffersize (bei Festplatten oder im Netzwerk sind 32-64K ganz in Ordnung, über langsame Internetleitungen etwas weniger (z.B. 4KB)
Dann machst du folgende Schleife (sorry ist VB.NET Code, muss aber in C# genauso gehen):

Dim Pos as Uint64=0
Dim Buffer(Buffersize-1) as Byte
While Pos<InputStream.Length
Dim Gelesen as Integer=InputStream.Read(Buffer,0,Math.Min(Buffer.Length,InputStream.Length-Pos))
OutputStream.Write(Buffer,0,Gelesen)
Pos+=Gelesen
End While

Die Variable Pos kannst du dann in einem Timer auswerten und die Arbeit in einem Thread erledigen. Um das wirst du sowieso nicht herumkommen, weil sonst das GUI einfriert. Den Timer kannst du gleich im Designer reinziehen und doppelklicken (Einschalten nicht vergessen, sonst tut er nicht, entweder im Code .Enabled=True oder gleich im Designer. In dem Timer kannst du dann auch gleich das ganze GUI aktualisieren. Ich schreibe z.B. Ausgaben in eine RTF-Box mit Statusmeldungen nie direkt rein, sondern in eine synchronisierte Queue: Queue.Synchronized(New Queue()
Alle 100ms wird dann alles ausgegeben. Das ist von der Performance auch wesentlich schneller bei vielen Ausgaben weil nicht alle 1ms immer ein Wort angehängt werden muss, sondern nur einmal alle 100m alles in einem Schwung ausgegeben werden kann.

Einen eigenen Thread kannst du so erzeugen:

Dim WorkerThread as new Thread(addressOf(THREAD_Work))
WorkerThread.Priority=Lowest
WorkerThread.Name="Worker"
WorkerThread.isBackground=True
WorkerThread.Start()

Public Sub THREAD_Work(ByVal Parameter as IrgendeineKlasse)
'Deine Kopieroperation
End Sub

3) Wenn du die Geschwindigkeit berechnest, dann solltest du immer auch den Fall abfragen, dass die Zeit 0 ist, wenn diese unter 15ms liegt. Dann dividierst du sonst durch 0.

4) Mit der .NET Kopierfunktion wird nicht aus einer Datei gelesen und in die andere geschrieben, sondern die ganze Datei kopiert mit allen Attributen, die irgendwie dranhängen (Rechte, Änderungsdatum, zusätzliche Dateistreams usw.) Beim Verschieben wird überhaupt nichts gelesen oder geschrieben, sondern auf der selben Partition nur ein Verzeichniseintrag umgehängt. Was schneller ist, kommt immer auf das Betriebssystem an. Bei Vista SP1 ist das Kopieren von großen Dateien optimiert und man wird da nie dran kommen. Bei XP ist man meistens manuell schneller, weil man optimieren kann. Auf einer Platte viel RAM als Cache verwenden, damit die Platte nicht so oft wechseln muss, von einer Platte auf die andere weniger, Cache, damit die Daten in den Write Cache von Windows passen.
 
Zuletzt bearbeitet:
lordfritte schrieb:
Und ich weiß jetzt auch warum sich die Dateigröße nicht ändert, weil bei File.Copy die Zieldatei schon von an fang an die volle Dateigröße hat.
die erklärung ist ganz einfach. dafür musst du nur eine datei öffnen, x byte nach vorn springen und ein '\0' o.ä. schreiben, dann ist die datei im explorer x byte groß. einfach und schnell. :)
 
Und was soll das bitte bringen, außer, dass sich die Datei unnötig fragmentiert? Der Speicher wird bei NTFS erst dann angefordert, wenn er wirklich benötigt wird und dann hast du wie bei manchen Filesharing Tools Dateien, die 100KB groß sind, aber nach dem Kopieren auf eine andere Platte ein paar GB.

Weiters sieht der Benutzer überhaupt nicht, ob die Datei schon fertig geschrieben ist. Beim Filesharing hat das ja vielleicht noch seine Berechtigung, da hier sowieso wahllos irgendwelche Stücke geschrieben werden, aber doch nicht beim sequentiellen Kopieren.

P.S.: Dateien, die man händisch öffnet, sollte man nachher auch wieder schließen, wenn man die ganze Zeit nur 0B hat ;-)
 
Ich würde an deiner Stelle den Puffer (iBytes) mal wesentlich größer stellen auf z.B. 64kb.

Des Weiteren hat dein Programm glaube ich einen kleinen Bug. Dein Puffer ist momentan ja 4kb groß. Bei der allerletzten Kopieraktion kann es aber vorkommen, dass fs.Read nur 2kb befüllt (weil die Datei zu Ende ist). sw.Write wird daher hier wohl etwas "Müll" schreiben. Praktischerweise gibt fs.Read aber zurück, wieviel tatsächlich gelesen wurde und somit würde es reichen, "iBytes = fs.Read(buffer, 0, iBytes);" zu schreiben. Dann kannst du dir übrigens auch das "if (iBytes > fileInfo.Length) iBytes = (int)fileInfo.Length;" sparen ;)

In dieser Schleife könntest du die Zeit auch einfach timen wobei du drauf achten solltest, dass die Ausgabe und Aufruf des Timers nicht bei jedem Schleifendurchgang passiert (kann man mit nem counter z.B. sicherstellen).

Ich würde die Schleife dann so in etwa schreiben:
PHP:
int count = 0;
if(fileInfo.Length > 0){
       while (iBytes > 0)
      {
        iBytes = fs.Read(buffer, 0, iBytes);
        sw.Write(buffer, 0, iBytes);
        ++count;
        if(count > 50){
           // hier Timer ausgeben
           count = 0;
        }
      }
}
 
Achtung: Die Länge, die man der Read Funktion bei .NET übergibt ist nur die maximale Länge. Die tatsächliche Länge kann variieren. Die tatsächlich gelesenen Bytes werden per Rückgabewert geliefert.
Dies kann nicht nur am Ende einer Datei auftreten, sondern immer. Die Read Funktion wartet solange, bis der Treiber Daten liefert und schreibt diese dann in das übergebene Array. Danach kehrt die Funktion sofort zurück, auch wenn die maximale Länge noch nicht erreicht ist. Übers Netzwerk ist diese Länge nach meiner Erfahrung ca. 73KB, kann aber abweichen.
Ein Rückgabewert von 0 heißt aber, dass man EOF erreicht hat. Dann braucht man nicht mehr weiter lesen und kann beenden.
Bei der Write Funktion werden jedoch immer alle Daten geschrieben.

Ich bin da beim ersten Mal auch lange gesessen und konnte nicht verstehen, warum mein Programm lokal und im Netzwerk funktionierte, aber übers Internet regelmäßig asynchron wurde mit der Übertragung, weil eben dort 2KB nicht immer in einem Schwung daher kommen.

Selbst bei Festplattenzugriffen konnte ich das Phänomen reproduzieren, jedoch dauert es deutlich länger bzw. benötigt größere Buffergrößen.

2.) Wenn man nicht so viele Daten auf einmal lesen kann z.B. weil man eine XML Datei empfängt, die auf Endtags geprüft werden muss oder man übers Netzwerk die Längeninformationen erst relativ spät erhält, dann empfiehlt sich ein BufferedStream. Bei diesem ist nicht bei jedem Zugriff ein Kontext Switch notwendig (ca. 100K Operationen/Sekunde), sondern nur ein paar Rechenoperationen und ein Function Call. Mit einer Read Länge von 16 Bytes kann ich locker mit 30-50MB/s lesen.
 
Zurück
Oben