2010-01-26 11 views
5

Ich möchte eine scheinbar einfache Frage stellen, dass ich die Antwort nirgendwo finden kann. Gibt es einen FAST modernen Algorithmus für die Dateieingabe und/oder -ausgabe, der mit allen standardkonformen C++ - Compilern kompiliert werden kann und für alle Betriebssysteme ohne die Notwendigkeit externer Bibliotheken funktioniert?Fast plattformübergreifender Algorithmus zum Lesen/Schreiben von Dateien in C++

  1. ich habe festgestellt, dass der schnellste Weg, Memory-Mapped-Dateien zu verwenden, aber das geht nicht, weil wir das gleiche Stück Code arbeitet auf allen Plattformen wollen
  2. wir nicht apis wie Win32-API verwenden können, Ursache, dass es plattformspezifische
  3. ich mag nicht, c verwenden, machen wird, ich will der Algorithmus nur reiner C++ Code mit stl sein, wenn möglich, nicht einig hässlich c mit vermischtem asm Hack/Trick
  4. Frameworks oder extern Bibliotheken, die nicht in standart C++ gehören, sollten nicht wie wxWidgets, Qt, MFC usw. verwendet werden.
  5. Das große empasis dieser ganzen Frage ist, dass der Algorithmus als FAST wie möglich ist, etwas entlang der Linien von der Geschwindigkeit mit Memory-Mapped-Dateien zu tun, noch schneller groß sein würde, aber ich weiß, das ist nicht möglich

Hast du jemals etwas so Verrücktes gesehen, das jemand außer mir erforscht hat? Ist ein solcher Algorithmus überhaupt möglich?

Vielen Dank für alle Empfehlungen

Antwort

9

mit folgenden Einschränkungen:

kann mit allen standardkonformen C++ Compiler und arbeitet für alle Betriebssysteme, ohne die Notwendigkeit von externen Bibliotheken erstellt werden?

Sie haben sich ziemlich auf die Standard-Bibliothek Datei IO-Funktionen beschränkt. Vielleicht POSIX-Funktionen (abhängig davon, welche Teilmenge von "allen standardkonformen C++ - Compilern" Sie in Erwägung ziehen).

Wenn sie nicht schnell genug für Sie sind, müssen Sie einige Einschränkungen fallen lassen.

+1

Einverstanden. Die einzige C++ - Standarddatei-E/A ist die aus der Standardbibliothek, d. Header-Datei '' oder ''. Wenn Sie schnelle Dateiein- und -ausgänge wünschen, ist das Wichtigste, ** Dateien nicht Byte für Byte zu lesen/schreiben, sondern große Blöcke auf einmal zu lesen/schreiben. – stakx

+0

kann die gesamte Datei auf einmal mit iostream gelesen werden? Würde das Leistungsvorsprung bieten, was ich nicht denke? ist nicht cstdio a c header? – user258883

+2

Das Lesen der gesamten Datei auf einmal ist immer noch langsamer als das Zuordnen in den Speicher. –

9

Das hat nichts mit einem "Algorithmus" zu tun.

Wenn es um das Schreiben von Daten in eine Datei geht, sind Sie dem Betriebssystem ausgeliefert. Speicherkarten sind "schnell", weil Sie nur in den Speicher schreiben und das Betriebssystem es wieder synchronisiert seine eigene Zeit. Wenn das Betriebssystem dies nicht unterstützt, haben Sie diesbezüglich kein Glück - es sei denn, Sie möchten eine eigene Speicherzuordnungsschicht implementieren.

Übrigens, POSIX hat mmap, wenn Sie sich also auf POSIX-kompatible Systeme beschränken, geht es Ihnen gut.

2

Einige Punkte:

  • Das hat nichts mit Algorithmen zu tun hat.
  • Die Ausrichtung auf ALLE Betriebssysteme ist kein sehr produktives Ziel (und es ist unmöglich
  • Ihr Code funktioniert nicht auf einer bestimmten Plattform, bis Sie es getestet haben). Stattdessen würde ich mich auf einige Betriebssysteme konzentrieren, die machbar sind - sagen wir POSIX + Win32.
  • In diesem Fall können Sie Speicherzuordnung tun, zum Beispiel durch die Implementierung von mmap() für Windows (über MapViewOfFile() usw.) - der Quellcode von git hat eine mmap-Implementierung für Windows, wenn Sie etwas Inspiration brauchen.
  • Wenn Sie die Speicherzuordnung nicht verwenden können, würde ich die normale C-Datei api anstelle von C++ 's Dateiströmen empfehlen, wenn Leistung eine große Sache ist. Obwohl die C++ - Streams das Potenzial für eine höhere Leistung für einige Operationen haben, ist es in der Praxis ziemlich viel langsamer.
  • Aber um eine gute Leistung zu erzielen, kann es oft "gut genug" sein, nur um sicherzustellen, dass Sie Ihre Daten auf vernünftige Weise behandeln. Lesen Sie Daten sequenziell, nicht neu lesen, etc. Perfekt ist der Feind von gut;)
+0

@kusma: Ich wäre daran interessiert, Leistungsvergleiche zwischen den 'stdio'-Funktionen von C und den 'iostream'-Funktionen von C++ zu sehen. Können Sie mich auf solche Ressourcen verweisen? Denn ich habe mir einmal den C++ 'iostream'-Bibliotheks-Quellcode angeschaut (genauer gesagt, die Implementierung, die mit MinGW geliefert wird) und hatte den Eindruck, dass es eigentlich nur ein sehr dünner (Template-basierter) Wrapper um den C-Datei-I/O ist Funktionen. Daher würde ich keinen signifikanten Leistungsunterschied erwarten. – stakx

+0

Der Feind Gottes? Huch ... :) –

+0

stakx: Leider nein. Die Ergebnisse sind lange verloren. Ich habe einige Male auf MSVC um 2001 herum profiliert, nachdem ein Freund von GCC/Linux erhebliche Verzögerungen bei der Verwendung von std :: iStream (im Vergleich zu den C APIs) berichtet hat. IIRC, fanden wir beide, dass die Leistungseinbußen etwa gleich waren, etwa 30%. Es mag sein, dass mir die Erinnerung an den Zahlen falsch ist. Drew: Sorry, ich meinte natürlich "der Feind von Satan";) – kusma

0

Die anderen Plakate sind richtig, dass die Leistung immer im Widerspruch zu Generizität (Cross-Plattform) ist.

Im Allgemeinen erhalten Sie jedoch die besten Ergebnisse, wenn Sie Ihre Eingaben "puffern" - mit fread() relativ große Datenblöcke einlesen und verarbeiten.

Ich weiß, das ist eine ziemlich grundlegende und allgemeine Antwort, aber das ist ungefähr so ​​spezifisch, wie Sie erhalten können, ohne entweder plattformspezifisch zu sein oder mehr über die spezifischen Eingaben zu wissen, die Sie verarbeiten.

1

Das sequentielle Lesen in Blöcken, die Vielfache (oder Potenzen von 2) der Blockgröße des Dateisystems sind, kann hilfreich sein. Dann nimm deine Daten auseinander, sobald der Block im Speicher ist. Irgendwo gibt es ein Whitepaper, in dem die Leistung für verschiedene Blockgrößen getestet wurde. Ich wünschte, ich könnte es wieder finden.

Sie könnten auch versuchen, einen dedizierten Thread zum Lesen von Blöcken aus der Datei, und einen anderen, der die Datenmanipulationen im Speicher (mit den richtigen Synchronisationen, natürlich). Dies ermöglicht es Ihnen, die CPU zu verwenden, um Daten zu verarbeiten, während Sie bei Ihren gelesenen Dateiaufrufen blockieren.

Wie auch immer, wenn Sie diese Ideen ausprobieren, lassen Sie uns bitte wissen, wenn Sie einen Unterschied bemerken. Die tatsächlichen Ergebnisse Ihrer Benchmark-Tests wären interessant.

+0

Die meisten Festplatten verwenden Blockgrößen, die ein Vielfaches von 512 Bytes sind. Bei den größeren Festplatten kann es 1024 oder größer sein. –

+0

Die von den Layern oben/unten verwendeten Puffergrößen können sich von denen des Dateisystems unterscheiden. Experimentiere mit verschiedenen Potenzen von 2 (beschränke dich nicht auf 512/1024, geh bis zu 256kB). Ich erinnere mich, dass 4kiB eine magische Zahl war, die in einer dieser Schichten in Linux verwendet wurde, aber ich erinnere mich nicht, wo. Entschuldigung, ich kann nicht genauer sein. –

+0

4k ist die Standardgröße eines Dateisystemblocks. –

1

Schnelle IO kocht im Allgemeinen auf zwei Dinge auf:

  1. Daten minimieren
  2. kernel/Benutzerkontext

Fast alle IO-Techniken zu adressieren die eine oder andere Schalt minimieren Kopieren versuchen. Der schnellste plattformübergreifende Code für IO, den ich kenne, ist das Perl IO-System. Ich würde vorschlagen, einen Blick auf the source. Perl-Hacker haben Jahrzehnte damit verbracht, ihre IO so schnell wie möglich auf so vielen Plattformen wie möglich zu bekommen.

4

Um eine andere Perspektive auf "Barmherzigkeit des Betriebssystems" zu setzen, liegt der größte Teil des Overhead beim Kopieren von Dateien beim Betriebssystem. Eine fragmentierte Datei benötigt mehr Zeit zum Lesen als eine defragmentierte Datei. Es gibt keine generischen oder Standard-C++ - Funktionen zum Erkennen fragmentierter Dateien.

Die schnellste Methode in C++:

std::ifstream in_file; 
std::ofstream out_file; 

out_file << in_file.rdbuf(); 

Sie weitere Informationen durch das Suchen im Internet mit den Suchbegriffen "Datei kopieren RDBUF" finden können. Das obige Fragment überlässt das Kopieren dem Betriebssystem, ist jedoch auf allen Plattformen übertragbar. Durch das Einlesen in die C++ - I/O-Streams können Sie die Größe des Lesepuffers festlegen oder einen eigenen Puffer verwenden.

Für das schnellere Kopieren von Dateien sind plattformspezifische Funktionen wie DMA-Übertragungen erforderlich. Die Verwendung von Threads und Mehrfachpufferung könnte dies beschleunigen. aber C++ hat keine Unterstützung für Threads (es gibt einen Defacto-Standard, POSIX, der Threads unterstützt). Ein Thread würde in Puffer lesen, während ein anderer Thread aus den Puffern schreibt.

Verwandte Themen