2015-08-05 13 views
9

Ich habe einige Probleme mit dem Schreiben in eine Datei - nämlich nicht in der Lage, schnell genug zu schreiben.Verbessern/optimieren Datei schreiben Geschwindigkeit in C++

Um zu erklären, ist mein Ziel, einen Datenstrom zu erfassen, der über Gigabit-Ethernet hereinkommt, und ihn einfach in einer Datei zu speichern.

Die Rohdaten werden mit einer Geschwindigkeit von 10MS/s empfangen und in einem Puffer gespeichert und anschließend in eine Datei geschrieben.

Nachfolgend finden Sie im entsprechenden Abschnitt des Code:

std::string path = "Stream/raw.dat"; 
    ofstream outFile(path, ios::out | ios::app| ios::binary); 

    if(outFile.is_open()) 
     cout << "Yes" << endl; 

    while(1) 
    { 
     rxSamples = rxStream->recv(&rxBuffer[0], rxBuffer.size(), metaData); 
     switch(metaData.error_code) 
     { 

      //Irrelevant error checking... 

      //Write data to a file 
       std::copy(begin(rxBuffer), end(rxBuffer), std::ostream_iterator<complex<float>>(outFile)); 
     } 
    } 

Das Problem, das ich bin der Begegnung ist, dass es zu lang nehmen die Proben in eine Datei zu schreiben. Nach etwa einer Sekunde meldet das Gerät, das die Proben sendet, dass sein Puffer übergelaufen ist. Nach einer kurzen Profilerstellung des Codes wird fast die gesamte Ausführungszeit auf std::copy(...) verbracht (99,96% der Zeit, um genau zu sein). Wenn ich diese Zeile entferne, kann ich das Programm stundenlang ausführen, ohne dass es zu einem Überlauf kommt.

Das sagte, ich bin eher ratlos, wie ich die Schreibgeschwindigkeit verbessern kann. Ich habe mehrere Beiträge auf dieser Website durchgesehen, und es scheint, als ob der gebräuchlichste Vorschlag (in Bezug auf die Geschwindigkeit) darin besteht, Dateischreibvorgänge zu implementieren, wie ich es bereits getan habe - durch die Verwendung von std::copy.

Wenn es hilfreich ist, führe ich dieses Programm auf Ubuntu x86_64. Irgendwelche Vorschläge würden geschätzt werden.

+2

Dies ist über eine USRP, ist es nicht –

+0

Intresting .... reine C Zeiger-ähnliche Richtung könnte Ihnen besser. Wenn Sie die Struktur Ihres Betriebssystems kennen, können Sie möglicherweise schneller auf den Speicher zugreifen. –

+0

Ja ... Ich benutze eine USRP N210. – Mlagma

Antwort

13

Also das Hauptproblem hier ist, dass Sie versuchen, in den gleichen Thread zu schreiben, wie Sie erhalten, was bedeutet, dass Ihre recv() kann nur wieder aufgerufen werden, nachdem die Kopie abgeschlossen ist. Ein paar Beobachtungen:

  • Verschieben Sie die Schrift in einen anderen Thread. Dies ist ein USRP, also könnte GNU Radio wirklich das Tool Ihrer Wahl sein - es ist von Natur aus Multithread.
  • Ihr Ausgabe-Iterator ist wahrscheinlich nicht die performanteste Lösung. Einfach "write()" zu einem Dateideskriptor könnte besser sein, aber das ist Performance-Messungen, die Ihnen überlassen sind
  • Wenn Ihre Festplatte/Dateisystem/OS/CPU nicht die Raten von der USRP kommen, Selbst wenn Sie das Entkoppeln von Thread-bezogenem Schreiben entkoppeln, dann gibt es nichts, was Sie tun können - erhalten Sie ein schnelleres System.
  • Versuchen Sie, einen RAM-Disk statt

Tatsächlich schreiben, ich weiß nicht, wie man mit dem std::copy Ansatz kam. Die rx_samples_to_file example that comes with UHD tut dies mit einem einfachen Schreiben, und Sie sollten definitiv bevorzugen, dass über das Kopieren; Datei-I/O kann auf guten Betriebssystemen oft mit einer Kopie weniger ausgeführt werden, und das Iterieren über alle Elemente ist wahrscheinlich sehr langsam.

+3

Vereinbar, Hinzufügen von mehr: Schreiben Sie die eingehenden Daten in einen oder mehrere große Puffer (abhängig von der Verzögerungszeit zwischen dem Empfangen von Daten und dem Schreiben in die Datei). Erstellen Sie einen Thread, der aus diesem Puffer liest und in die Datei schreibt (in ** riesigen Blöcken **). Verwenden Sie außerdem so viel Hardwareunterstützung wie möglich, z. B. DMA. –

+0

@ThomasMatthews Vereinbaren im Allgemeinen, größere Datenblöcke = bessere Leistung, aber das hat auch Nachteile, nämlich, wenn die Chunks nicht zu groß werden, ist das OS möglicherweise nicht beschäftigt für überlange Verarbeitung dieser, und auf Systemen, wo CPU Kerne sind spärlich, die Zeit, in der ein Betriebssystem mit Datei-I/O beschäftigt ist, kann kritisch werden, wenn es nicht mithalten kann, Daten gleichzeitig durch das Netzwerk zu erhalten. Linux skaliert ziemlich gut auf mehreren Kernen, das ist also wirklich nur ein Problem bei Single-Core-CPUs. –

+0

@ThomasMatthews Ich wechselte zu "schreiben" wie du es vorgeschlagen hast, und es ist eine enorme Verbesserung - es ist noch zu überlaufen. Ich habe auch die Größe meines Puffers erhöht. – Mlagma

4

Machen wir ein bisschen Mathe.

Ihre Proben sind (anscheinend) vom Typ std::complex<std::float>. Bei einem (typischen) 32-Bit-Float bedeutet das, dass jeder Abtastwert 64 Bits umfasst. Bei 10 MS/s bedeutet dies, dass die Rohdaten etwa 80 Megabyte pro Sekunde sind - das liegt in dem, was Sie auf eine Desktop-Festplatte (7200 RPM) schreiben können, aber ziemlich nahe an der Grenze (normalerweise 100) -100 Megabyte pro Sekunde oder so).

Leider schreiben Sie trotz der std::ios::binary die Daten tatsächlich im Textformat (weil std::ostream_iterator grundsätzlich stream << data; tut).

Dies verliert nicht nur an Genauigkeit, sondern erhöht auch die Größe der Daten, zumindest in der Regel. Die genaue Höhe des Anstiegs hängt von den Daten ab - ein kleiner ganzzahliger Wert kann tatsächlich die Datenmenge verringern, aber für willkürliche Eingaben ist eine Größenzunahme nahe bei 2: 1 ziemlich üblich. Mit einer Erhöhung von 2: 1 sind Ihre ausgehenden Daten jetzt rund 160 Megabyte/Sekunde - was schneller ist, als die meisten Festplatten verarbeiten können.

Der offensichtliche Ausgangspunkt für eine Verbesserung wäre es, die Daten im binären Format zu schreiben statt:

uint32_t nItems = std::end(rxBuffer)-std::begin(rxBuffer); 
outFile.write((char *)&nItems, sizeof(nItems)); 
outFile.write((char *)&rxBuffer[0], sizeof(rxBuffer)); 

Im Moment ich sizeof(rxBuffer) unter der Annahme verwendet habe, dass es ein echtes Array ist. Wenn es sich tatsächlich um einen Zeiger oder Vektor handelt, müssen Sie die richtige Größe berechnen (was Sie wollen, ist die Gesamtzahl der zu schreibenden Bytes).

Ich würde auch bemerken, dass Ihr Code ein so ernstes Problem hat, da er kein Trennzeichen zwischen Elementen beim Schreiben der Daten festgelegt hat, werden die Daten ohne etwas zu trennen geschrieben ein Gegenstand von der nächsten. Das heißt, wenn Sie zwei Werte von (zum Beispiel) 1 und 0.2 geschrieben haben, würden Sie nicht in 1 und 0.2 zurückgelesen, sondern einen einzelnen Wert von 10.2. Das Hinzufügen von Trennzeichen zu Ihrer Textausgabe fügt einem Prozess, der bereits fehlschlägt, da er zu viele Daten generiert, noch mehr Aufwand hinzu (etwa 15% mehr Daten).

Schreiben im Binärformat bedeutet, dass jeder Float genau 4 Bytes verbraucht, daher sind Trennzeichen nicht notwendig, um die Daten korrekt wieder einzulesen.

Der nächste Schritt danach wäre, zu einer niedrigeren Datei-E/A-Routine abzusteigen. Je nach Situation kann dies einen Unterschied machen oder auch nicht. Unter Windows können Sie FILE_FLAG_NO_BUFFERING angeben, wenn Sie eine Datei mit CreateFile öffnen. Dies bedeutet, dass Lese- und Schreibvorgänge in dieser Datei den Cache umgehen und direkt auf die Festplatte gehen.

In Ihrem Fall ist das wahrscheinlich ein Gewinn - bei 10 MS/s werden Sie wahrscheinlich den Cache-Speicher ziemlich lange verbrauchen, bevor Sie die gleichen Daten erneut lesen. In einem solchen Fall werden Sie durch das Zulassen von Daten in den Cache praktisch nichts erhalten, aber Sie müssen einige Daten in den Cache kopieren und dann etwas später auf die Festplatte kopieren. Schlimmer noch, es ist wahrscheinlich, den Cache mit all diesen Daten zu verschmutzen, so dass es keine anderen Daten mehr speichert, die viel mehr vom Caching profitieren.