C# File.Copy() geschwindigkeit?

lordfritte

Lieutenant
Registriert
Juli 2006
Beiträge
973
File.Copy() geschwindigkeit ermitteln?

Hallo ich bin es mal wieder mit neuen tollen problemen :D
Also wie die überschrift schon sagt möchte ich eine Datei von A nach B kopieren, dabei interessiert mich aber der Kopierdurchsatz, lässt sich der ermitteln?
Also mich intererssieren die x Bytes / Sec
 
Zuletzt bearbeitet:
Wohl kaum... da wirst du wohl kaum um eine eigene Implementierung drum rum kommen...
 
@claW.: Aha, und wo genau ist die Geschwindigkeitsberechnung?
Das ist kein Beispiel, höchstens ein Ansatz...
 
1668mib schrieb:
@claW.: Aha, und wo genau ist die Geschwindigkeitsberechnung?
es wird nach jedem stück kopieren eine zahl erhöht, welche irgendwann an der eigentlichen dateigröße angekommen ist. die geschwindigkeit (also kb/s) kann man dann ja wohl selbst berechnen, indem man eine neue variable einführt und diese jede sekunde zurücksetzt und davor immer das maximum nimmt (nur ein ansatz), welches demzufolge der anzahl an kopierten bytes pro sekunde entspricht. das wäre das einzige, was man dafür noch selbst hinzufügen müsste. der rest steht ja schon.

wie will man es sonst machen und einen status abfragen (außer die funktion gibt selbst parameter mit, welche sich ändern)? ich kenne keine möglichkeit, demzufolge wird es wohl auch nicht anders gehen (um es mit einem status des fortschritts anzuzeigen).

ich hab ihm auch lediglich ein beispiel gegeben (es ist ein beispiel wie man es lösen kann, ein ansatz wäre etwas unvollständiges ;)). den grundlegenden gedanken dahinter zu finden und dies selbst nachzubauen bzw. es zu portieren ist zumindest nicht so schwer.
 
Das Kopieren zu programmieren ist ja der leichte Teil, interessanter ist, wie man die Zeitmessung vernünftig umsetzt, denn das Zeitintervall sollte weder zu kurz noch zu lange gewählt sien (vor allem sollte es Zeitintervalle geben, die für die Messung herangezogen werden, so wie du das erklärst würdest du die Geschwindigkeit über den kompletten Kopiervorgang mitteln, aber das ist ja nicht wirklich interessant...)

und wie man es sonst macht? Ganz einfach: Das Stichwort heißt Threads...
damit lässt sich sowieso noch mehr machen (Kopiervorgang abbrechen z.B., weil das komplette Programm ja nicht vom Kopiervorgang blockiert wird)
 
1668mib schrieb:
wie man die Zeitmessung vernünftig umsetzt, denn das Zeitintervall sollte weder zu kurz noch zu lange gewählt sien
denkst du denn das würde extrem abweichen? ich denke nicht, dass eine festplatte länger als eine sekunde braucht um z.b. 2 mb einzulesen (oder wieviel man halt angegeben hat) und im puffer zu speichern oder direkt zu schreiben. die "vorzeigekopierfunktion" (= explorer) macht es doch genauso wenig gut, denn erstens werden nur kleinste blöcke beim kopieren gelesen und geschrieben (siehe vergleich zu fastcopy, wo das kopieren erheblich verkürzt werden kann) und zweitens schwankt die zeitanzeige (welche ja im prinzip mit der kopiergeschwindigkeit zu tun hat). und ob nun 500 kb mehr oder weniger angezeigt werden, wäre für mich (beim lesen/schreiben von/auf festplatten) unerheblich. man muss die funktion ja nicht unnötig aufblähen, es sei denn man entwickelt eine anwendung professionell.
 
Das macht durchaus einen Unterschied. Kopier doch mal eine Datei mit mehreren hundert MB und dann zwischendurch noch paar andere - die Performance wird sich merklich ändern... eine Anzeige, die auf sowas nicht reagiert, kann man gleich weg lassen, denn dann bringt sie dem Nutzer eh nichts.

@bearnator: Ist zwar spät, aber so komplett am Thema vorbei zu schreiben, muss man erst mal schaffen... *kopfschüttel*

Edit: Ach so, tut mir leid, ich hab gedacht er möchte, dass die aktuelle Übertragungsrate angezeigt wird...
naja in de Fall lässt sich das ganze ja problemlos mit der Methode Copy() realisieren, davor eben die Zeit merken, die Zeit danach abfragen, Differenz in Sekunden berechnen und durch die Dateigröße teilen...
 
Zuletzt bearbeitet:
Ich habe mir auch schon überlegt eine eigene funktion zu scheiben, so sieht die aus:
Code:
private bool Copy(string sSourc, string sDest, long lPos, int iBytes, out long lTime)
    {
      lTime = 0;

      bool bCopy = true;
      FileStream fs = new FileStream(sSourc, FileMode.Open, FileAccess.Read);
      FileStream sw = new FileStream(sDest, FileMode.Append, FileAccess.Write);
      Stopwatch timer = new Stopwatch();

      fs.Position = lPos;
      if (fs.Position + iBytes > fs.Length)
      {
        iBytes = (int)(fs.Length - (long)fs.Position);
        bCopy = false;
      }

      if (iBytes <= 0)
        return false;

      byte[] buffer = new byte[iBytes];

      timer.Start();
      fs.Read(buffer, 0, iBytes);
      sw.Write(buffer, 0, iBytes);
      timer.Stop();
      lTime = timer.ElapsedMilliseconds;

      fs.Close();
      sw.Close();

      return bCopy;
    }

Wegen der Zeitmessung muss ich noch gucken, bei lTime kommt immer nur 0 oder 1 raus.
Was mir nur stört ist das diese Funktion zu lahm ist. Mein 2. Beispiel:
Code:
string s1 = @"D:\opensource_dvd12.iso";
      string s2 = @"E:\opensource_dvd12.iso";
      string s3 = @"E:\opensource_dvd12_2.iso";
      long lPos = 0;
      long iMilliseconds = 0;
      Stopwatch sw1 = new Stopwatch();
      Stopwatch sw2 = new Stopwatch();

      sw1.Start();
      while (Copy(s1, s2, lPos, 4096, out iMilliseconds))
      {
        lPos += 4096;
      }
      sw1.Stop();

      sw2.Start();
      File.Copy(s1, s3);
      sw2.Stop();


      Console.WriteLine(sw1.Elapsed.ToString());
      Console.WriteLine(sw2.Elapsed.ToString());

Bei meiner Funktion kommt bei einer 711 MB großen Datei 54 Sekunden raus und bei File.Copy() 10 Sekunden, der Unterschied ist schon Enorm.
 
Die schlechte Performance ist ja wohl klar wenn du für jedes 4kb-Stückchen deine eigene Copy-Funktion neu aufrufst und dabei auch noch die Filestreams jedes Mal neu anlegst und die Position neu suchst! In der "while"-Kopierschleife sollten möglichst wenig Operationen sein und diese sollten möglichst direkt drin und nicht in ner externen Funktion sein. Des Weiteren sollten alle in der Schleife verwendeten Variablen wenn möglich wieder verwendet werden und nicht dauernd neu angelegt werden.
 
Ich hab nun von C# nicht so viel Ahnung, aber in php/js (AJAX), wenn eine Datei hochgeladen wird, prüfe ich einfach periodisch, wie groß die Kopie der Datei aktuell ist.

Keine Ahnung, ob das auch in C# funktioniert, aber kann man nicht einfach während des kopierens jede Sekunde oder so die Größe der Kopie abfragen, und so prüfen, um wieviel die Datei in der letzten Sekunde gewachsen ist? Das müsste dann natürlich in einem eigenen Thread ablaufen, der kurz vor Copy() gestartet wird - zumindest nehme ich mal einfach an, dass die Copy-Methode den aktuellen Thread blockiert.
 
Ich habe auch schon versucht vor einem File.Copy einen Timer zu satrten:

Code:
void timer_Elapsed(object sender, ElapsedEventArgs e)
    {
      FileInfo file = new FileInfo(s3);
      long lBytesPerSecond = (file.Length - _lLastSize);
      _lLastSize = lBytesPerSecond;
      Console.WriteLine("{0} kb/s, {1}, {2}", lBytesPerSecond / 1024, file.Length, _lLastSize);
    }

Aber die Anzeige aktualliert sich nicht. Also es kommt immer das selbe raus.
 
Läuft der Timer in einem eigenen Thread, d.h. er wird ausgeführt während der Kopiervorgang ausgeführt wird?
 
Oh das könnte das Problem sein oder? Also der Timer läuft nicht in einem eignen Thread.
 
lordfritte schrieb:
Also der Timer läuft nicht in einem eignen Thread.

Doch Timer laufen immer in einem eigenen Thread ;) , warum sich die Anzeige nicht aktualisiert kann ich so leider auch ned sagen.
 
naja egal, mir geht es allein um eine Zeitschätzung also kann ich das auch anhand der verbliebenen Dateien machen.
Aber ich werde noch ein wenig experimentieren.
Aber was mich noch interessieren würde ist folgendes: Ich habe einen Quad-Core Prozessor, würde es etwas bringen wenn ich in 4 Threads immer gleichzeitig 4 Dateien kopiere?

Aber jetzt fällt mir ein, ein Timer wird also immer in einem Thread ausgeführt, wozu gibt es dann einen Timer und einen ThreadTimer?
 
Zuletzt bearbeitet:
lordfritte schrieb:
Aber was mich noch interessieren würde ist folgendes: Ich habe einen Quad-Core Prozessor, würde es etwas bringen wenn ich in 4 Threads immer gleichzeitig 4 Dateien kopiere?
nein, die festplatte limitiert komplett alles. selbst nachbauen kannst du das auch, indem du fastcopy 4 mal ausführst (natürlich volle geschwindigkeit) mit jeweils > 100 mb dateien. dann merkst du wie dein system förmlich "zusammen bricht".
 
Aber ich frage mich warum die File.Copy() Funktion von Microsoft so schnell ist.
 
Weils ein nativer System-Call ist vermutlich. Aber normalerweise kriegst du das auch einigermaßen hin WENN dus richtig machst (zwar mit etwas mehr CPU-Auslastung aber durchaus vergleichbar). Wie sieht denn jetzt dein Code KOMPLETT aus? Meist hilft es auch, wenn du größere Stückchen auf einmal kopierst.
 
Mein Code sieht jetzt so aus:
PHP:
class CopySample : ISample
  {
    long _lLastSize = 0;
    string s1 = @"D:\opensource_dvd12.iso";
    string s2 = @"E:\Test1.Iso";
    string s3 = @"E:\Test2.iso";

    Thread copyThread = null;
    Thread copyWatcher = null;

    #region ISample Member

    public string Name
    {
      get { return "CopySample"; }
    }

    public void Main(string[] args)
    {
      //copyThread = new Thread(new ThreadStart(CopyThread));
      //copyThread.IsBackground = true;
      //copyThread.Start();

      //copyWatcher = new Thread(new ThreadStart(CopyWatcher));
      //copyWatcher.IsBackground = true;
      //copyWatcher.Start();

      Stopwatch sw1 = new Stopwatch();
      Stopwatch sw2 = new Stopwatch();

      sw1.Start();
      File.Copy(s1, s2);
      sw1.Stop();
      Console.WriteLine("{0}", sw1.Elapsed);


      sw2.Start();
      Copy(s1, s3);
      sw2.Stop();
      Console.WriteLine("{0}", sw2.Elapsed);
    }


    private void CopyThread()
    {
      File.Copy(s1, s3);
    }

    private void CopyWatcher()
    {
      while (copyThread.IsAlive)
      {
        if(File.Exists(s3))
        {
          FileInfo info = new FileInfo(s3);
          Console.WriteLine(info.Length);
        }
        Thread.Sleep(250);
      }
    }

    private void Copy(string sSourc, string sDest)
    {
      int iBytes = 4096;
      long lCurrentPos = 0;
      FileStream fs = new FileStream(sSourc, FileMode.Open, FileAccess.Read);
      FileStream sw = new FileStream(sDest, FileMode.Append, FileAccess.Write);
      FileInfo fileInfo = new FileInfo(sSourc);

      if (iBytes > fileInfo.Length)
        iBytes = (int)fileInfo.Length;

      byte[] buffer = new byte[iBytes];

      while (lCurrentPos < fileInfo.Length)
      {
        fs.Read(buffer, 0, iBytes);
        sw.Write(buffer, 0, iBytes);
        lCurrentPos += iBytes;
      }

      fs.Close();
      sw.Close();
    }

    #endregion
  }

Und ich glaube ich habs, die Datei ist 711 MB Groß
File.Copy(): 24.81
Meine FUnktion: 25.86

Aber naja war ein zufall, bei einem 2. Test mit der gleichen Datei hänge ich 5 Sekunden nach und die Zieldatei ist auch irgendwie anders.

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.
 
Zuletzt bearbeitet:
Zurück
Oben