double to string ohne wissenschaftliche Notation oder nachgestellte Nullen, effizient

9

Diese Routine wird unzählige Male aufgerufen, um große csv-Dateien voller Zahlen zu erstellen. Gibt es einen effizienteren Weg dazu?

%Vor%     
tpascale 01.03.2013, 19:37
quelle

3 Antworten

8

Bevor Sie beginnen, prüfen Sie, ob in dieser Funktion viel Zeit verbraucht wird. Tun Sie dies, indem Sie entweder mit einem Profiler oder auf andere Weise messen. Zu wissen, dass Sie es eine Zillion Mal nennen, ist alles sehr gut, aber wenn es sich herausstellt, dass Ihr Programm nur 1% seiner Zeit in dieser Funktion ausgibt, dann kann nichts, was Sie hier tun, Ihre Programmleistung um mehr als 1% verbessern. Wenn das der Fall wäre, wäre die Antwort auf Ihre Frage "für Ihre Zwecke nein, diese Funktion kann nicht wesentlich effizienter gemacht werden und Sie verschwenden Ihre Zeit, wenn Sie es versuchen".

Als erstes meiden Sie s.substr(0, s.size()-1) . Dies kopiert den größten Teil der Zeichenkette und es macht Ihre Funktion für NRVO ungeeignet, also denke ich, dass Sie normalerweise eine Kopie bei der Rückkehr bekommen werden. Also die erste Änderung, die ich machen würde, ist die letzte Zeile zu ersetzen mit:

%Vor%

Aber wenn Leistung eine ernsthafte Sorge ist, dann ist hier, wie ich es tun würde. Ich verspreche nicht, dass dies die schnellste Möglichkeit ist, aber es vermeidet einige Probleme mit unnötigen Zuweisungen und Kopieren. Jeder Ansatz, der stringstream involviert, wird eine Kopie vom Stringstream zum Ergebnis benötigen, also wollen wir eine Operation auf niedrigerer Ebene, snprintf .

%Vor%

Der zweite Aufruf von snprintf setzt voraus, dass std::string zusammenhängenden Speicher verwendet. Dies ist in C ++ 11 garantiert. Es ist nicht in C ++ 03 garantiert, aber gilt für alle aktiv gepflegten Implementierungen von std::string , die dem C ++ - Committee bekannt sind. Wenn Leistung wirklich wichtig ist, dann ist es vernünftig, diese nicht-portable Annahme zu machen, da das direkte Schreiben in eine Zeichenkette später das Kopieren in eine Zeichenkette speichert.

s.pop_back() ist der C ++ 11-Weg, um s.erase(s.end()-1) zu sagen, und s.back() ist s[s.size()-1]

Für eine weitere mögliche Verbesserung könnten Sie den ersten Aufruf von snprintf loswerden und stattdessen Ihre s auf einen Wert wie std::numeric_limits<double>::max_exponent10 + 14 (im Grunde die Länge, die -DBL_MAX hat) Bedürfnisse). Das Problem ist, dass dies viel mehr Speicher reserviert und löscht, als normalerweise benötigt wird (322 Bytes für ein IEEE-Double). Meine Intuition ist, dass dies langsamer sein wird als der erste Aufruf von snprintf , ganz zu schweigen von verschwenderischem Speicher, wenn der String-Rückgabewert eine Weile vom Aufrufer gehalten wird. Aber du kannst es immer testen.

Alternativ berechnet std::max((int)std::log10(d), 0) + 14 eine relativ enge Obergrenze für die benötigte Größe und ist möglicherweise schneller als snprintf kann sie genau berechnen.

Schließlich kann es sein, dass Sie die Leistung verbessern können, indem Sie die Funktionsschnittstelle ändern. Anstatt beispielsweise eine neue Zeichenfolge zurückzugeben, könnten Sie sie an eine Zeichenfolge anhängen, die vom Aufrufer übergeben wurde:

%Vor%

Dann kann der Aufrufer reserve() viel Platz haben, rufen Sie Ihre Funktion mehrmals (vermutlich mit anderen String-Anhängen dazwischen), und schreiben Sie den resultierenden Datenblock auf einmal in die Datei, ohne eine andere Speicherbelegung als die %Code%. "Plenty" muss nicht die ganze Datei sein, es könnte jeweils eine Zeile oder ein "Absatz" sein, aber alles, was eine Unmenge von Speicherzuweisungen vermeidet, ist ein potenzieller Leistungsschub.

    
Steve Jessop 01.03.2013, 21:31
quelle
4

Effizient in Bezug auf Geschwindigkeit oder Kürze?

%Vor%

Zeigt "1" an. Formatiert bis zu signifikanten 16 Ziffern ohne nachfolgende Nullen, bevor die wissenschaftliche Notation wiederhergestellt wird.

    
BSalita 22.12.2013 12:04
quelle
1
  • Verwenden Sie snprintf und ein Array von char anstelle von stringstream und string
  • übergibt einen Zeiger an char buffer an dbl2str, in den er gedruckt wird (um zu vermeiden, dass der Kopierkonstruktor von string beim Zurückgeben aufgerufen wird). Stellen Sie die zu druckende Zeichenfolge in einem Zeichenpuffer zusammen (oder konvertieren Sie den Zeichenpuffer beim Aufrufen in eine Zeichenfolge oder fügen Sie ihn einer vorhandenen Zeichenfolge hinzu)
  • deklarieren Sie die Funktion inline in einer Header-Datei

    %Vor%
Andre Holzner 01.03.2013 19:48
quelle

Tags und Links