2010-08-18 15 views
6

Ich habe eine sehr große Menge von Binärdateien, wo mehrere tausend rohe Frames von Video sequenziell gelesen und verarbeitet werden, und ich bin jetzt auf der Suche nach optimieren, wie es scheint, mehr CPU-gebunden als I/O-gebunden.. NET Binärdatei lesen Leistung

Die Rahmen werden derzeit auf diese Weise gelesen, und ich vermute, dies ist der größte Übeltäter ist:

private byte[] frameBuf; 
BinaryReader binRead = new BinaryReader(FS); 

// Initialize a new buffer of sizeof(frame) 
frameBuf = new byte[VARIABLE_BUFFER_SIZE]; 
//Read sizeof(frame) bytes from the file 
frameBuf = binRead.ReadBytes(VARIABLE_BUFFER_SIZE); 

es einen großen Unterschied in .NET Würde die I/O neu zu organisieren, um zu vermeiden, Erstellen all dieser neuen Byte-Arrays mit jedem Frame?

Mein Verständnis von .NET-Speicherzuordnungsmechanismus ist schwach, da ich von einem reinen C/C++ - Hintergrund komme. Meine Idee ist, dies neu zu schreiben, um eine statische Pufferklasse zu teilen, die einen sehr großen geteilten Puffer mit einer Ganzzahl enthält, die die tatsächliche Größe des Rahmens verfolgt, aber ich mag die Einfachheit und Lesbarkeit der aktuellen Implementierung und würde sie lieber behalten, wenn CLR behandelt das bereits auf eine Weise, die mir nicht bewusst ist.

Jeder Eingang würde sehr geschätzt werden.

+5

Haben Sie einen Profiler ausgeführt, um sicherzustellen, dass der Leistungseinbruch nicht aus anderen Quellen stammt? Oder bist du einfach gegangen und hast angenommen "So ist es wahrscheinlich"? –

+0

Hallo David, Ich lief den Leistungsprofiler darauf ein paar Mal und diese bestimmte Methode ist meine teuerste. Daher schaue ich, ob diese "new byte []" Methode ein offensichtlicher Performance-Killer in .NET ist. Als C-Programmierer sieht dies analog zu Tausenden von "malloc" -Anweisungen für jeden Puffer aus, was definitiv langsamer als ein wiederverwendetes Puffer wäre. – rnd

Antwort

7

Sie müssen frameBuf nicht initialisieren, wenn Sie binRead.ReadBytes verwenden - Sie erhalten ein neues Byte-Array zurück, das das gerade erstellte überschreibt. Dadurch wird jedoch für jeden Lesevorgang ein neues Array erstellt.

Wenn Sie nicht mehrere Byte-Arrays erstellen möchten, können Sie binRead.Read verwenden, wodurch die Bytes in ein Array eingefügt werden, das Sie angeben. Wenn andere Threads das Array verwenden, sehen sie jedoch, dass sich der Inhalt direkt vor ihnen ändert. Stellen Sie sicher, dass Sie sicherstellen können, dass Sie mit dem Puffer fertig sind, bevor Sie ihn erneut verwenden.

+0

Danke, dass Sie darauf hingewiesen haben - ich bin mir sicher, dass meine redundante Zuteilung dies merklich verlangsamt.Und das statische Shared-Array ist genau das, was ich in Betracht ziehe, aber wenn der Leistungsgewinn verglichen mit dem Erstellen der Byte-Arrays nicht groß ist, würde ich lieber bei der eleganten Lösung für genau die Komplikation bleiben, die Sie skizzieren (gemeinsamer Zugriff) . – rnd

1

Sie müssen hier vorsichtig sein. Es ist sehr einfach, völlig gefälschte Testergebnisse für Code wie diesen zu erhalten, Ergebnisse, die nie im wirklichen Gebrauch repro reproduzieren. Das Problem ist der Dateisystemcache, er speichert die Daten, die Sie aus einer Datei lesen. Das Problem beginnt, wenn Sie Ihren Test immer wieder ausführen, den Code optimieren und nach Verbesserungen suchen.

Die zweite und nachfolgende Male führen Sie den Test, die Daten nicht mehr von der Festplatte. Es ist immer noch im Cache vorhanden, es braucht nur eine Kopie von Speicher zu Speicher, um es in Ihr Programm zu bekommen. Das ist sehr schnell, ungefähr eine Mikrosekunde Overhead plus die Zeit, die zum Kopieren benötigt wird. Das läuft mit Busgeschwindigkeiten, mindestens 5 Gigabyte pro Sekunde auf modernen Maschinen.

Ihr Test zeigt nun, dass Sie viel Zeit für die Zuweisung des Puffers und die Verarbeitung der Daten aufwenden, relativ zum Zeitaufwand für das Lesen der Daten.

Dies wird selten repro im realen Einsatz. Die Daten sind noch nicht im Cache, jetzt muss das Sluggy-Laufwerk die Daten suchen (viele Millisekunden) und es muss vom Plattenspieler gelesen werden (höchstens ein paar Dutzend Megabyte pro Sekunde). Das Lesen der Daten dauert jetzt gut drei von vier Größen der Zeit länger. Wenn es Ihnen gelungen ist, den Bearbeitungsschritt doppelt so schnell zu machen, läuft Ihr Programm nur noch 0,05% schneller. Geben oder nehmen.

+0

Das ist ein guter Punkt, aber ich führe meine Tests mit einem Dataset durch, das den Arbeitsspeicher meiner Maschine um einige Gigabyte in den Schatten stellt. Was mich beunruhigt, ist die Tatsache, dass ein ähnlicher Code in meiner alten C++ - Bibliothek diesen Datensatz in weniger als der Hälfte der Zeit verarbeiten würde. Ich habe jedoch bemerkt, dass das Profil warnt, dass ca. 2.826 Seiten pro Sekunde auf die Festplatte geschrieben werden und dass die App speichergebunden sein kann. Ich besitze keine dieser Arrays explizit - könnten diese zwischengespeichert werden, bevor der GC sie rückgängig macht? – rnd

+2

Diese Puffer sind wahrscheinlich groß, mehr als 85 KB. Welche bekommt sie in der LOH zugeordnet. Sie werden für eine Weile dort feststecken, es dauert eine Gen # 2 Sammlung. Nichts ist kostenlos, die Wiederverwendung von Puffern, wenn sie groß sind, ist eine gute Strategie in .NET. –

+0

Wenn Sie das Laden der Datei von der Festplatte erzwingen möchten, löschen Sie den Windows-Dateicache, wie in dieser Frage zu sehen ist: http://stackoverflow.com/q/478340/80525 – BKewl