Wie kann ich das verbessern/ersetzen, sprintf, die habe ich gemessen, um eine performance-hotspot?
Durch profiling habe ich entdeckt, dass der sprintf hier dauert eine lange Zeit. Gibt es eine leistungsfähigere alternative, die immer noch Griffen die führenden Nullen in der y/m/d h/m/s-Felder?
SYSTEMTIME sysTime;
GetLocalTime( &sysTime );
char buf[80];
for (int i = 0; i < 100000; i++)
{
sprintf(buf, "%4d-%02d-%02d %02d:%02d:%02d",
sysTime.wYear, sysTime.wMonth, sysTime.wDay,
sysTime.wHour, sysTime.wMinute, sysTime.wSecond);
}
Hinweis: Der OP erklärt in den Kommentaren, dass dies eine abgespeckte Beispiel. Die "echte" Schleife enthält zusätzlichen code, der verwendet unterschiedliche Zeit-Werten aus einer Datenbank. Profiling hat nicht aufgezeigt sprintf()
als der Täter.
- Wie lange ist eine "längere Zeit"? Ich würde erwarten, dass es in Mikrosekunden statt Millisekunden (je nach CPU)
- Sie wären besser dran, einen Weg zu finden, rufen sprintf weniger Häufig statt.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Wenn Sie schreiben Sie Ihre eigene Funktion, um die Arbeit zu tun, eine lookup-Tabelle der string-Werte von 0 .. 61 würde vermeiden, dass zu tun, keine Arithmetik für alles abgesehen von dem Jahr.
edit: Beachten Sie, dass zur Bewältigung Schaltsekunden (und passend
strftime()
) Sie sollten in der Lage sein zu drucken, die Sekunden-Werte von 60 und 61.Alternativ, wie etwa
strftime()
? Ich habe keine Ahnung, wie man die performance vergleicht (es könnte auch sein Aufruf von sprintf()), aber es ist einen Blick Wert (und es tun könnte, die über lookup-selbst).Könnten Sie versuchen, füllen jeden char in der Ausgabe in der Reihe.
... etc...
Nicht schön, aber Sie erhalten das Bild. Wenn nichts anderes, kann es helfen zu erklären, warum sprintf nicht so schnell.
OTOH, vielleicht könntest du den cache das Letzte Ergebnis. So würde man nur benötigen, zu generieren jede Sekunde.
Printf braucht, um mit einer Vielzahl von Formaten.
Sie sicherlich könnte schnappen Sie sich die Quelle für printf und verwenden Sie es als basis zu Rollen Sie Ihre eigene version, die sich speziell mit der sysTime Struktur. So übergeben Sie in einem argument, und es nicht nur genau die Arbeit, die getan werden muss und nichts mehr.
Was meinst du mit "langer" Zeit -- seit der
sprintf()
ist die einzige Anweisung in der Schleife und die "Heizung" der Schleife (Inkrement, Vergleich) ist vernachlässigbar, diesprintf()
hat verbrauchen die meiste Zeit.Denken Sie an die alten Witz über den Mann, der verlor seinen Ehering an der 3rd Street eine Nacht, aber sah es am 5. da das Licht heller ist es? Sie haben gebaut, ein Beispiel, das entworfen, um zu "beweisen" Ihre Annahme, dass
sprintf()
ist wirkungsloser.Ihre Ergebnisse werden genauer, wenn Sie Profil "aktuelle" code enthält
sprintf()
zusätzlich zu allen anderen Funktionen und algorithmen, die Sie verwenden. Alternativ schreiben Sie Ihre eigene version, die Adressen der spezifischen null-gepolsterte numerische Konvertierung, die Sie benötigen.Können Sie an den Ergebnissen überrascht sein.
Sieht aus wie Jaywalker ist, was auf eine sehr ähnliche Methode (schlagen Sie mich, indem Sie weniger als eine Stunde).
Zusätzlich zu den bereits vorgeschlagenen lookup-Tabelle-Methode (n2s[] array unten), wie etwa die Erstellung von format-Puffer, so dass die üblichen sprintf weniger intensiv ist? Der code unten nur die zu füllen in der minute und Sekunde, jedes mal durch die Schleife, es sei denn, das Jahr/Monat/Tag/Stunde geändert haben. Offensichtlich, wenn diese sich geändert haben, die Sie tun, nehmen Sie ein anderes sprintf Treffer, aber insgesamt ist es vielleicht nicht mehr als das, was Sie derzeit erleben (in Verbindung mit der array-lookup).
Wie etwa das Zwischenspeichern der Ergebnisse? Ist das nicht eine Möglichkeit? Wenn man bedenkt, dass diese Besondere sprintf () - Aufruf gemacht wird, auch oft in deinem code, ich gehe davon aus, dass zwischen den meisten dieser aufeinander folgenden Anrufe, die Jahr, Monat und Tag nicht ändern.
Somit realisieren wir für Sie etwas wie das folgende. Deklarieren Sie eine alte und eine aktuelle SYSTEMTIME-Struktur:
Auch erklären separate Teile zu halten das Datum und die Zeit:
Für die erste Zeit, müssen Sie füllen in beiden sysTime, oldSysTime sowie datePart-argument und die timePart. Aber die nachfolgenden sprintf()'s gemacht werden kann, ziemlich schneller wie unten angegeben:
Obige code hat einige Redundanz, um den code leichter zu verstehen. Sie können Faktor einfach raus. Sie können sich weiter beschleunigen, wenn Sie wissen, dass sogar die Stunde und die Minuten nicht schneller ändern, als Ihr Aufruf an die routine.
Ich würde ein paar Dinge tun...
printf
-Familie-Funktionen ist die format-string-parsing, und es ist dumm zu widmen, die Zyklen zu analysieren, die auf jede Ausführung einer Schleife.{ "00", "01", "02", ..., "99" }
). Dies ist, weil Sie vermeiden wollen moduluar Arithmetik, und eine 2-byte-Tabelle bedeutet, dass Sie nur verwenden Sie eine modulo, für das Jahr.Würden Sie wahrscheinlich bekommen w perf Steigerung von hand Rollen einer routine, die aus legt die Ziffern in der return buf, da könnte man vermeiden, immer wieder analysieren einer format-Zeichenfolge und würde sich nicht mit viel mehr komplexe Fälle sprintf Griffe. Ich bin verabscheuen, um tatsächlich zu empfehlen, dass, obwohl.
Ich würde empfehlen, versuchen, herauszufinden, ob Sie irgendwie reduzieren den Betrag, den Sie brauchen, zu generieren, diese strings, sind Sie optional somegtimes, können Sie zwischengespeichert werden, etc.
Ich bin auf ein ähnliches problem im moment.
Muss ich log debug-Befehle mit Zeitstempel, Dateiname, Zeilennummer usw. auf einem embedded-system. Wir haben bereits einen logger im Ort, aber wenn ich den Drehknopf auf "full-logging", es frisst alle unsere proc Zyklen und stellt unser system in dire states, Staaten, die keine EDV-Gerät sollte jemals zu erleben.
Jemand gesagt hat "Man kann nicht Messen/beobachten, ohne etwas verändert, was man Messen/beobachten."
So, ich bin, Dinge zu verändern um die Leistung zu verbessern. Der aktuelle Stand der Dinge ist, dass ich 2x schneller als das original Funktionsaufruf (der Flaschenhals in das logging-system ist nicht in der Funktion nennen, aber in der log-reader ist eine separate EXE-Datei, die kann ich entsorgen, wenn ich Schreibe mein eigenes logging-stack).
Die Schnittstelle, die ich zur Verfügung stellen müssen, ist so etwas wie-
void log(int channel, char *filename, int lineno, format, ...)
. Ich muss anfügen, die Kanal-Namen (der zurzeit noch ein lineare Suche innerhalb einer Liste! Für jede einzelne debug-Anweisung!) und timestamp inklusive Millisekunden-Zähler. Hier sind einige der Dinge Im tun, um diese schneller-strcpy
anstatt in der Liste suchen. define-makroLOG(channel, ...etc)
alslog(#channel, ...etc)
. Sie könnenmemcpy
wenn Sie fix die Länge der Zeichenfolge durch die DefinitionLOG(channel, ...)
log("...."#channel - sizeof("...."#channel) + *11*)
behoben 10 byte channel-LängenWas über die drei Ziffern (Millisekunden) und fünf Ziffern (lineno)? I dont like itoa und ich nicht wie die custom itoa (
digit = ((value /= value) % 10)
), weil entweder die divs und mods sind langsam. Ich schrieb die folgenden Funktionen und später entdeckt, dass etwas ähnliches in der AMD-Optimierung Handbuch (in der Montage), die gibt mir das Vertrauen, dass diese über die Schnellste C-Implementierungen.Ebenso für die Zeilennummern,
Insgesamt, mein code ist ziemlich schnell jetzt. Die
vsnprintf()
ich dauert etwa 91% der Zeit, und der rest von meinem code dauert nur 9% (wobei der rest des Codes, d.h. außervsprintf()
verwendet, um 54% früher)Den zwei schnell-Formatierer, die ich getestet habe sind FastFormat und Karma::generieren (Teil Steigern Geist).
Könnten Sie finden es auch nützlich, es zum benchmark-oder zumindest den Blick für vorhandene benchmarks.
Beispielsweise diese eine (obwohl es fehlt FastFormat):
StringStream ist der Vorschlag, habe ich von Google.
http://bytes.com/forum/thread132583.html
Es ist schwer vorstellbar, dass Sie gehen zu schlagen sprintf bei der Formatierung von zahlen. Sind Sie sicher, dass sprintf ist dein problem?