Wie parsen Space-getrennte Floats in C ++ schnell?
Ich habe eine Datei mit Millionen von Zeilen, jede Zeile hat 3 schwimmt durch Leerzeichen voneinander getrennt. Es braucht eine Menge Zeit, die Datei zu Lesen, also versuchte ich, Sie zu Lesen, über die memory-mapped-Dateien, nur um herauszufinden, dass das problem nicht mit der Geschwindigkeit der IO, sondern mit der Geschwindigkeit der Analyse.
Meine aktuelle Analyse ist es, den stream (aufgerufene Datei), und führen Sie die folgenden
float x,y,z;
file >> x >> y >> z;
Jemand in Stack-Überlauf empfohlen, die Verwendung von Boost.Geistes, aber konnte ich nicht finden keine einfache Anleitung, um zu erklären, wie es zu benutzen.
Ich versuche zu finden, eine einfache und effiziente Art und Weise zu analysieren, eine Linie, die wie folgt aussieht:
"134.32 3545.87 3425"
Ich wirklich zu schätzen einige helfen. Ich wollte mit strtok, um es zu teilen, aber ich weiß nicht, wie konvertieren von strings, floats, und ich bin mir nicht ganz sicher, dass es der beste Weg.
Mir nichts aus, wenn die Lösung zu Steigern oder nicht. Ich habe nichts dagegen, wenn es nicht die effizienteste Lösung überhaupt, aber ich bin mir sicher, dass es möglich ist, das doppelte der Geschwindigkeit.
Vielen Dank im Voraus.
InformationsquelleAutor der Frage OopsUser | 2013-07-04
Du musst angemeldet sein, um einen Kommentar abzugeben.
Wenn die Umwandlung der Flaschenhals ist (was durchaus möglich ist),
Sie sollten beginnen, indem du die verschiedenen Möglichkeiten in der
standard. Logisch, würde man erwarten, dass Sie sehr nah zu sein,
aber praktisch sind Sie nicht immer:
Haben Sie bereits festgestellt, dass
std::ifstream
ist zu langsam.Konvertieren Sie Ihre memory-mapped Daten zu einem
std::istringstream
ist fast sicher nicht eine gute Lösung; Sie werden zunächst
erstellen Sie ein string, das wird kopieren Sie alle Daten.
Schreiben Sie Ihre eigenen
streambuf
zu Lesen direkt aus dem Speicher,ohne Sie zu kopieren (oder mit der veraltet
std::istrstream
)könnte eine Lösung sein, obwohl, wenn das problem wirklich ist
Umwandlung... dies immer noch verwendet die gleiche Konvertierung Routinen.
Können Sie immer versuchen
fscanf
oderscanf
auf Ihrem memory-mappedstream. Abhängig von der Implementierung, Sie könnte schneller sein
als die verschiedenen
istream
Implementierungen.Wahrscheinlich schneller als jede von diesen ist die Verwendung
strtod
. Keine Notwendigkeitfür die tokenisierung:
strtod
überspringt führende Leerzeichen(einschließlich
'\n'
), und hat einen out-parameter, wo es stellt denAdresse des ersten Zeichens nicht Lesen. Die Ende-Bedingung ist
ein bisschen schwierig, dein loop sollte wohl sehen ein bisschen aus wie:
Wenn keine dieser schnell genug sind, müssen Sie berücksichtigen die
die tatsächlichen Daten. Es hat wahrscheinlich eine Art von zusätzliche
Einschränkungen, was bedeutet, dass Sie kann potenziell zu schreiben
eine Konvertierungs-routine, die schneller ist als die Allgemeine;
z.B.
strtod
zu handhaben sind sowohl Feste als auch wissenschaftliche, und eshat zu 100% genau, auch wenn es 17 signifikanten stellen.
Es ist auch die locale-spezifisch. All dies wird Hinzugefügt
Komplexität, was bedeutet, dass zusätzlichen code auszuführen. Aber Vorsicht:
schreiben Sie eine effiziente und korrekte Konvertierung routine, auch für
eine eingeschränkte Menge von Eingabe -, ist nicht trivial; Sie haben wirklich zu
wissen, was Sie tun.
EDIT:
Nur aus Neugier, ich habe einige tests. Zusätzlich zu den
die vorgenannten Lösungen, die ich schrieb eine einfache benutzerdefinierte Konverter,
der nur Griffe festen Punkt (nicht wissenschaftlich), mit am meisten
fünf Ziffern nach dem Komma, und der Wert vor dem dezimal
muss sich in ein
int
:(Wenn Sie tatsächlich nutzen diese, sollten Sie auf jeden Fall fügen Sie einige Fehler
die Handhabung. Dies war nur klopfte schnell für experimentelle
Zwecke, zum Lesen der Datei test würde ich erzeugt, und nichts
sonst.)
Das interface ist genau das der
strtod
zur Vereinfachung der Codierung.Lief ich die benchmarks in beiden Umgebungen (auf verschiedenen Maschinen,
also die absoluten Werte aller Zeiten sind nicht relevant). Ich habe die
folgende Ergebnisse:
Unter Windows 7 kompiliert mit VC-11 (/O2):
Unter Linux 2.6.18 kompilieren mit g++ 4.4.2 (-O2, IIRC):
In allen Fällen, bin ich beim Lesen 554000 Linien mit jeweils 3 nach dem Zufallsprinzip
generiert floating point im Bereich
[0...10000)
.Das auffälligste ist der enorme Unterschied zwischen den
fstream
undfscan
unter Windows (und der relativ kleineUnterschied zwischen
fscan
undstrtod
). Die zweite Sache istwie viel die einfache benutzerdefinierte Funktion zur Umsetzung erhält, auf
beide Plattformen. Die notwendige Fehlerbehandlung würde es langsam nach unten
ein wenig, aber der Unterschied ist immer noch signifikant. Ich erwartete
einige Verbesserung, da es nicht eine Menge Dinge behandeln, die
die standard-Konvertierung Routinen (wie dem wissenschaftlichen format,
sehr, sehr kleine Zahl, - Inf und NaN, i18n, etc.), aber nicht diese
viel.
InformationsquelleAutor der Antwort James Kanze
Zusammenfassung:
Spirit-Parser sind am schnellsten. Wenn Sie können, verwenden Sie C++14 betrachten Sie die experimentelle version Spirit X3:
Die oben genannten Maßnahmen mit Hilfe von memory-mapped-Dateien. Mit IOstreams, es wird langsamer über den Vorstand,
aber nicht so langsam wie
scanf
mit C/POSIXFILE*
Funktion ruft:Was folgt, ist die Teile von der ALTEN Antwort
Umgebung:
Vollständigen Code
Vollständigen code zu den alten benchmark ist in der Bearbeiten Geschichte von diesem postdie neueste version ist auf github
InformationsquelleAutor der Antwort sehe
Bevor Sie beginnen, stellen Sie sicher, dass dies der langsame Teil Ihrer Applikation und erhalten eine Testumgebung um ihn herum, damit Sie Messen können, Verbesserungen.
boost::spirit
wäre übertrieben für diese, meiner Meinung nach. Versuchenfscanf
InformationsquelleAutor der Antwort Jeff Foster
Ich würde check out this post Mit ifstream Lesen schwimmt oder Wie kann ich die tokenisierung ein string in C++ besonders die Beiträge im Zusammenhang zu C++ - String-Toolkit-Bibliothek. Ich habe C strtok, C++ - streams, Boost-tokenizer und die besten von Ihnen für die Leichtigkeit und verwenden Sie C++ - String-Toolkit-Bibliothek.
InformationsquelleAutor der Antwort DannyK
einen nitty-gritty Lösung wäre, zu werfen, mehr Kerne auf das problem, Laich mehrere threads.
Wenn der Flaschenhals ist nur die CPU kann man halbieren, unten die Laufzeit und erzeugt zwei threads (auf multicore-CPUs)
einige andere Tipps:
versuchen zu vermeiden, parsing-Funktionen aus der Bibliothek wie boost-und/oder std. Sie sind aufgeblasen mit der Fehlerüberprüfung Bedingungen und viel der Bearbeitungszeit damit verbracht, diese Prüfungen. Nur für ein paar Konvertierungen sind Sie gut, aber Versagen kläglich, wenn es um Prozess-Millionen-Werte. Wenn Sie bereits wissen, dass Ihre Daten gut formatiert, dass Sie schreiben können (oder finden) Sie eine benutzerdefinierte optimierte C-Funktion, die nicht nur die Konvertierung der Daten
verwenden Sie einen großen Zwischenspeicher (sagen wir mal 10 MB), in dem Sie laden die chunks der Datei und führen Sie die Konvertierung auf es
divide et impera: teilen Sie Ihr problem in kleinere, einfacher: Vorverarbeiten Ihrer Datei haben, machen es single-line single-float, split jede Zeile, indem Sie die ". " - Zeichen und konvertieren Ganzzahlen anstelle von float, dann verschmelzen die zwei Ganzzahlen zu erstellen, die float-Zahl
InformationsquelleAutor der Antwort Gianluca Ghettini
Ich glaube, einer der wichtigsten Regel in der string-Verarbeitung ist "nur einmal gelesen, ein Zeichen zu einem Zeitpunkt". Es ist immer einfacher, schneller und sicherer, denke ich.
Ich einfach das benchmark-Programm, um zu zeigen, wie einfach es ist. Mein test sagt, dieser code läuft 40% schneller als
strtod
version.Unten ist die Ausgabe der Konsole von i7 Mac Book Pro (kompiliert in XCode 4.6).
InformationsquelleAutor der Antwort 9dan
mit C ist die Schnellste Lösung.
aufsplitten in Token mitzu konvertieren float mitstrtok
und dannstrtof
. Oder wenn Sie wissen das genaue format verwendenfscanf
.InformationsquelleAutor der Antwort log0