Kann jemand die Leistung meiner Ganzzahl mit dem unten verlinkten std :: string-Code übertreffen?
Es gibt bereits mehrere Fragen, die erklären, wie eine Ganzzahl in eine std::string
in C ++ konvertiert wird , wie diese , aber keine der bereitgestellten Lösungen ist effizient.
Hier ist kompilierbereiter Code für einige gängige Methoden, mit denen Sie konkurrieren können:
- Der "C ++ Weg" mit Stringstream: http://ideone.com/jh3Sa
- sprintf, den SO-er normalerweise leistungsbewussten empfehlen: http://ideone.com/82kwR
Entgegen der landläufigen Meinung verfügt es boost::lexical_cast
über eine eigene Implementierung ( Whitepaper ) und verwendet keine stringstream
numerischen Einfügeoperatoren. Ich würde wirklich gerne sehen, wie die Leistung verglichen wird, denn diese andere Frage legt nahe, dass es miserabel ist .
Und mein eigener Beitrag, der auf Desktop-Computern wettbewerbsfähig ist und einen Ansatz demonstriert, der im Gegensatz zu Algorithmen, die von Integer-Modulo abhängen, auch auf eingebetteten Systemen mit voller Geschwindigkeit ausgeführt wird:
- Bens Algorithmen: http://ideone.com/SsEUW
Wenn Sie diesen Code verwenden möchten, werde ich ihn unter einer vereinfachten BSD-Lizenz zur Verfügung stellen (kommerzielle Nutzung zulässig, Namensnennung erforderlich). Fragen Sie einfach.
Schließlich ist die Funktion ltoa
nicht standardisiert, aber weit verbreitet.
- ltoa-Version für alle, die einen Compiler haben, der sie bereitstellt (ideone nicht): http://ideone.com/T5Wim
Ich werde meine Leistungsmessungen in Kürze als Antwort veröffentlichen.
Regeln für Algorithmen
- Geben Sie Code für eine Konvertierung von mindestens 32-Bit-Ganzzahlen mit und ohne Vorzeichen in Dezimalzahlen an.
- Ausgabe als
std::string
. - Keine Tricks, die mit Threading und Signalen nicht kompatibel sind (z. B. statische Puffer).
- Sie können einen ASCII-Zeichensatz annehmen.
- Stellen Sie sicher, dass Sie Ihren Code auf
INT_MIN
einem Zweierkomplement-Computer testen, auf dem der absolute Wert nicht darstellbar ist. - Idealerweise sollte das Ausgabezeichen für Zeichen identisch mit der kanonischen C ++ Version verwenden
stringstream
, http://ideone.com/jh3Sa , aber alles , was als die richtige Zahl ist in Ordnung, zu klar verständlich ist. - NEU : Obwohl Sie für den Vergleich alle gewünschten Compiler- und Optimierungsoptionen (außer vollständig deaktiviert) verwenden können, muss der Code auch unter mindestens VC ++ 2010 und g ++ kompiliert und korrekte Ergebnisse liefern.
Hoffnungsvolle Diskussion
Neben besseren Algorithmen möchte ich auch einige Benchmarks auf verschiedenen Plattformen und Compilern erhalten (verwenden wir den MB / s-Durchsatz als Standardmaßeinheit). Ich glaube, dass der Code für meinen Algorithmus (ich weiß, dass der sprintf
Benchmark einige Verknüpfungen benötigt - jetzt behoben) ein standardmäßig genau definiertes Verhalten ist, zumindest unter der ASCII-Annahme, aber wenn Sie ein undefiniertes Verhalten oder Eingaben sehen, für die die Ausgabe erfolgt ist ungültig, bitte darauf hinweisen.
Schlussfolgerungen:
Für g ++ und VC2010 werden unterschiedliche Algorithmen ausgeführt, wahrscheinlich aufgrund der unterschiedlichen Implementierungen von std::string
jeweils. VC2010 macht mit NRVO eindeutig einen besseren Job, da es nur bei gcc hilfreich ist, die Wertrendite loszuwerden.
Es wurde festgestellt, dass Code sprintf
um eine Größenordnung besser abschneidet. ostringstream
fällt um den Faktor 50 und mehr zurück.
Der Gewinner der Herausforderung ist user434507, der Code erstellt, der 350% meiner eigenen Geschwindigkeit auf gcc ausführt. Weitere Einträge sind aufgrund der Launen der SO-Community geschlossen.
Die aktuellen (endgültigen?) Speed Champions sind:
- Für gcc: user434507 8-mal schneller als
sprintf
: http://ideone.com/0uhhX - Für Visual C ++: Timo 15-mal schneller als
sprintf
: http://ideone.com/VpKO3
quelle
Antworten:
Dies wird auf Systemen explodieren, die nicht ausgerichtete Speicherzugriffe nicht zulassen (in diesem Fall die erste nicht ausgerichtete Zuweisung über
*(short*)
einen Segfault verursachen), sollte aber ansonsten sehr gut funktionieren.Eine wichtige Sache zu tun ist, die Verwendung von zu minimieren
std::string
. (Ironisch, ich weiß.) In Visual Studio beispielsweise sind die meisten Aufrufe von Methoden von std :: string nicht inline, selbst wenn Sie in den Compileroptionen / Ob2 angeben. Selbst etwas so Triviales wie ein Aufruf vonstd::string::clear()
, von dem Sie erwarten können, dass es sehr schnell ist, kann 100 Clockticks beim Verknüpfen von CRT als statische Bibliothek und bis zu 300 Clockticks beim Verknüpfen als DLL benötigen.Aus dem gleichen Grund ist die Rückgabe per Referenz besser, da eine Zuweisung, ein Konstruktor und ein Destruktor vermieden werden.
quelle
sprintf
. Und mit VC ++ 2010 erreicht es ungefähr 50 MB / s, ungefähr die doppelte Geschwindigkeit von sprintf.sprintf
Implementierung verwendet, die ich bereits in meiner Frage erwähnt habe, aber ich glaube, dass der Code-to-Beat genau das gleiche Ergebnis liefert wie Stringstream.sprintf
Wrapper repariert, um Verwirrung zu vermeiden.Ah, übrigens eine großartige Herausforderung ... Ich hatte viel Spaß damit.
Ich muss zwei Algorithmen einreichen (Code befindet sich unten, wenn Sie Lust haben, dorthin zu springen). In meinen Vergleichen muss die Funktion einen String zurückgeben und int und unsigned int verarbeiten können. Das Vergleichen von Dingen, die keine Zeichenfolge erstellen, mit denen, die dies tun, ist nicht wirklich sinnvoll.
Die erste ist eine unterhaltsame Implementierung, die keine vorberechneten Nachschlagetabellen oder explizite Division / Modulo verwendet. Dieser ist konkurrenzfähig mit den anderen mit gcc und mit allen außer Timo auf msvc (aus einem guten Grund, den ich unten erkläre). Der zweite Algorithmus ist meine tatsächliche Vorlage für höchste Leistung. In meinen Tests schlägt es alle anderen sowohl auf gcc als auch auf msvc.
Ich denke, ich weiß, warum einige der Ergebnisse bei MSVC sehr gut sind. std :: string hat zwei relevante Konstruktoren
std::string(char* str, size_t n)
und
std::string(ForwardIterator b, ForwardIterator e)
gcc macht dasselbe für beide ... das heißt, es verwendet den zweiten, um den ersten zu implementieren. Der erste Konstruktor kann wesentlich effizienter implementiert werden, und MSVC tut dies. Der Nebeneffekt davon ist, dass in einigen Fällen (wie meinem schnellen Code und Timos Code) der String-Konstruktor eingebunden werden kann. Tatsächlich ist das Umschalten zwischen diesen Konstruktoren in MSVC für meinen Code fast ein zweifacher Unterschied.
Meine Leistungstestergebnisse:
Codequellen:
- Voigt
- Timo
- ergosys
- user434507
- Benutzer-voigt-timo
- Hopman-fun
- Hopman schnelle
gcc 4.4.5 -O2 unter Ubuntu 10.10 64-Bit, Core i5
MSVC 2010 64-Bit / Ox unter Windows 7 64-Bit, Core i5
Hier sind einige Ergebnisse und ein Test- / Timing-Framework für ideone
http://ideone.com/XZRqp
Beachten Sie, dass ideone eine 32-Bit-Umgebung ist. Meine beiden Algorithmen leiden darunter, aber hopman_fast ist zumindest immer noch wettbewerbsfähig.
Beachten Sie, dass ich für diejenigen, die keine Zeichenfolge erstellen, die folgende Funktionsvorlage hinzugefügt habe:
Nun zu meinem Code ... zuerst der lustige:
Und dann der schnelle:
quelle
Benchmark-Daten für den in der Frage angegebenen Code:
Auf ideone (gcc 4.3.4):
Core i7, Windows 7 64-Bit, 8 GB RAM, Visual C ++ 2010 32-Bit:
cl /Ox /EHsc
Core i7, Windows 7 64-Bit, 8 GB RAM, Visual C ++ 2010 64-Bit:
cl /Ox /EHsc
Core i7, Windows 7 64-Bit, 8 GB RAM, cygwin gcc 4.3.4:
g++ -O3
bearbeiten : Ich wollte meine eigene Antwort hinzufügen, aber die Frage wurde geschlossen, also füge ich sie hier hinzu. :) Ich habe meinen eigenen Algorithmus geschrieben und es geschafft, eine anständige Verbesserung gegenüber Bens Code zu erzielen, obwohl ich ihn nur in MSVC 2010 getestet habe. Ich habe auch einen Benchmark aller bisher vorgestellten Implementierungen erstellt, wobei ich das gleiche Test-Setup verwendet habe, das auch in Bens Original enthalten war Code. - Timo
Intel Q9450, Win XP 32 Bit, MSVC 2010
cl /O2 /EHsc
- -
quelle
std::string
oder um eine schlechte Optimierung des arithmetischen Codes handelt. Ich werde eine andere Version machen, diestd::string
am Ende nicht konvertiert wird und sehen, ob gcc besser abschneidet.Obwohl die Informationen, die wir hier für die Algorithmen erhalten, ziemlich nett sind, denke ich, dass die Frage "kaputt" ist, und ich werde erklären, warum ich das denke:
In der Frage wird nach der Leistung der Konvertierung
int
-> gefragt.std::string
Dies kann beim Vergleich einer allgemein verfügbaren Methode von Interesse sein, z. B. bei verschiedenen Stringstream-Implementierungen oder boost :: lexical_cast. Es ist jedoch nicht sinnvoll, nach neuem Code , einem speziellen Algorithmus, zu fragen , um dies zu tun. Der Grund dafür ist, dass int2string immer eine Heap-Zuweisung von std :: string beinhaltet. Wenn wir versuchen, den letzten Teil unseres Konvertierungsalgorithmus herauszuholen, halte ich es nicht für sinnvoll, diese Messungen mit den von std vorgenommenen Heap-Zuweisungen zu verwechseln: : string. Wenn ich eine performante Konvertierung möchte, verwende ich immer einen Puffer mit fester Größe und ordne auf keinen Fall etwas auf dem Heap zu!Zusammenfassend denke ich, dass die Timings aufgeteilt werden sollten:
Diese Aspekte sollten meiner Meinung nach nicht zu einem Zeitpunkt verwechselt werden.
quelle
std::string
Anforderung "Ausgabe als " dort platziert, um die Dinge für alle Einreichungen fair und konsistent zu machen. Algorithmen, die schnellerstd::string
Ergebnisse erzielen, füllen auch einen vorab zugewiesenen Puffer schneller.Ich kann nicht unter VS testen, aber dies scheint schneller zu sein als Ihr Code für g ++, ungefähr 10%. Es könnte wahrscheinlich abgestimmt werden, die gewählten Entscheidungswerte sind Vermutungen. Nur int, sorry.
quelle
char
aufunsigned
eine ähnliche Geschwindigkeitsverbesserung in meinem Code bewirkt, zumindest unter gcc / ideone ideone.com/uthKK . Ich werde morgen auf VS testen.Die Antwort von user2985907 wurde aktualisiert ... modp_ufast ...
quelle
Mismatch found: Generated: -99 Reference: -9099999 Mismatch found: Generated: -99 Reference: -9099998 Mismatch found: Generated: -99 Reference: -9099997
Hier ist mein kleiner Versuch dieses lustigen Puzzles.
Anstatt Nachschlagetabellen zu verwenden, wollte ich, dass der Compiler alles herausfindet. Insbesondere in diesem Fall - wenn Sie Hackers 'Delight lesen, sehen Sie, wie Divide und Modulo funktionieren - ist es sehr gut möglich, dies mithilfe von SSE / AVX-Anweisungen zu optimieren.
Leistungsbenchmark
Mein Benchmark hier sagt mir, dass es 1,5-mal schneller ist als die Arbeit von Timo (auf meinem Intel Haswell läuft es mit ungefähr 1 GB / s).
Dinge, die man als Betrüger betrachten könnte
Was den Cheat angeht, bei dem ich keinen Standard-String mache - das habe ich natürlich auch für meinen Benchmark von Timos Methode berücksichtigt.
Ich benutze ein intrinsisches: BSR. Wenn Sie möchten, können Sie stattdessen auch DeBruijn-Tabellen verwenden - eines der Dinge, über die ich in meinem 'schnellsten 2log'-Beitrag ziemlich viel geschrieben habe. Natürlich hat dies eine Leistungsbeeinträchtigung (* na ja ... wenn Sie viele itoa-Operationen durchführen, können Sie tatsächlich eine schnellere BSR erzielen, aber ich denke, das ist nicht fair ...).
So funktioniert es
Als erstes müssen Sie herausfinden, wie viel Speicher wir benötigen. Dies ist im Grunde ein 10log, das auf verschiedene intelligente Arten implementiert werden kann. Siehe die häufig zitierten " Bit Twiddling Hacks"Einzelheiten finden ".
Als nächstes müssen Sie die numerische Ausgabe ausführen. Ich verwende dafür die Vorlagenrekursion, damit der Compiler es herausfindet.
Ich benutze 'modulo' und 'div' direkt nebeneinander. Wenn Sie Hacker's Delight lesen, werden Sie feststellen, dass die beiden eng miteinander verbunden sind. Wenn Sie also eine Antwort haben, haben Sie wahrscheinlich auch die andere. Ich dachte, der Compiler kann die Details herausfinden ... :-)
Der Code
Ermitteln der Anzahl der Ziffern mithilfe eines (geänderten) Protokolls10:
Holen Sie sich die Zeichenfolge:
quelle
Ich habe das eine Weile herumgesessen und bin endlich dazu gekommen, es zu posten.
Ein paar mehr Methoden im Vergleich zum Doppelwort hopman_fast . Die Ergebnisse beziehen sich auf den für kurze Zeichenfolgen optimierten std :: string des GCC, da sonst Leistungsunterschiede durch den Overhead des Verwaltungscodes für das Kopieren beim Schreiben verdeckt werden. Der Durchsatz wird auf die gleiche Weise wie an anderer Stelle in diesem Thema gemessen. Die Zykluszahlen gelten für die rohen Serialisierungsteile des Codes, bevor der Ausgabepuffer in eine Zeichenfolge kopiert wird.
Kompilierungszeitschalter:
-DVSTRING - aktiviert SSO-Zeichenfolgen für ältere GCC-Setups
-DBSR1 - aktiviert schnelles log10
-DRDTSC - aktiviert Zykluszähler
quelle
Ich glaube, ich habe den schnellsten Integer-to-String-Algorithmus erstellt. Es ist eine Variante des Modulo 100-Algorithmus, die etwa 33% schneller ist, und vor allem für kleinere und große Zahlen. Es heißt Script ItoS-Algorithmus. Lesen Sie das Dokument, in dem erklärt wird, wie ich den Algorithmus entwickelt habe, unter https://github.com/kabuki-starship/kabuki-toolkit/wiki/Engineering-a-Faster-Integer-to-String-Algorithm . Sie können den Algorithmus verwenden, aber denken Sie darüber nach, einen Beitrag zur Kabuki-VM zu leisten, und lesen Sie das Skript . insbesondere, wenn Sie an AMIL-NLP- und / oder softwaredefinierten Netzwerkprotokollen interessiert sind.
Autor
quelle
std::string
werden, damit der Vergleich mit anderen hier aufgeführten Methoden gültig ist. Anfangs konnte ich die Verwendung des Verschiebungsoperators für den binären Suchbaum nicht herausfinden, da ein Vergleich bereits außergewöhnlich schnell ist, aber jetzt ist mir klar, dass er nützlich wäre, um diesen verschobenen Wert vorab zu berechnen, wenn Sie ihn benötigen. Du benutzt es aber nicht. Auf der anderen Seite erhalten Sie keine großen Literale, die in Anweisungen codiert sind. Vielleicht ist das allein Grund genug.Änderung der Lösung von user434507. Geändert, um ein Zeichenarray anstelle einer C ++ - Zeichenfolge zu verwenden. Läuft etwas schneller. Außerdem wurde der Scheck für 0 im Code nach unten verschoben ... da dies in meinem speziellen Fall nie vorkommt. Bewegen Sie es zurück, wenn es in Ihrem Fall häufiger vorkommt.
quelle
Mismatch found: Generated: -9999999990 Reference: -999999999 Mismatch found: Generated: -9999999980 Reference: -999999998 Mismatch found: Generated: -9999999970 Reference: -999999997
Wir verwenden den folgenden Code (für MSVC):
Templated tBitScanReverse:
char / wchar_t Helfer:
Befugnisse von 10 Tischen:
Aktueller Druck:
Letzte Schleife kann abgewickelt werden:
Die Hauptidee ist dieselbe wie die zuvor vorgeschlagene von @atlaste: https://stackoverflow.com/a/29039967/2204001
quelle
Bin gerade wegen der jüngsten Aktivitäten darauf gestoßen; Ich habe nicht wirklich Zeit, Benchmarks hinzuzufügen, aber ich wollte hinzufügen, was ich in der Vergangenheit geschrieben habe, wenn ich eine schnelle Konvertierung von Ganzzahlen in Zeichenfolgen benötige ...
https://github.com/CarloWood/ai-utils/blob/master/itoa.h
https://github.com/CarloWood/ai-utils/blob/master/itoa.cxx
Der hier verwendete Trick besteht darin, dass der Benutzer ein std :: -Array bereitstellen muss, das groß genug ist (auf seinem Stapel), und dass dieser Code die Zeichenfolge rückwärts in diese schreibt, beginnend bei den Einheiten, und dann einen Zeiger mit einem Versatz in das Array zurückgibt dorthin, wo das Ergebnis tatsächlich beginnt.
Dies reserviert oder verschiebt daher keinen Speicher, erfordert jedoch eine Division und ein Modulo pro Ergebnisziffer (was meiner Meinung nach schnell genug ist, da dies lediglich Code ist, der intern auf der CPU ausgeführt wird; Speicherzugriff ist normalerweise das Problem imho).
quelle
Warum verwendet niemand die div-Funktion von stdlib, wenn sowohl Quotient als auch Rest benötigt werden?
Mit Timos Quellcode kam ich zu folgendem Ergebnis:
Ok, für vorzeichenlose Ints kann die div-Funktion nicht verwendet werden, aber vorzeichenlose Ints können separat behandelt werden.
Ich habe das COPYPAIR-Makro wie folgt definiert, um Variationen beim Kopieren der beiden Zeichen aus den digit_pairs zu testen (bei keiner dieser Methoden wurde ein offensichtlicher Vorteil festgestellt):
quelle