Schnelle Exp-Berechnung: mögliche, um Genauigkeit zu verbessern, ohne zu viel Leistung?
Ich versuche aus dem schnellen Exp(x) Funktion, die vorher beschrieben wurde, diese Antwort auf eine Frage ALSO auf die Verbesserung der Geschwindigkeit der Berechnung, die in C#:
public static double Exp(double x)
{
var tmp = (long)(1512775 * x + 1072632447);
return BitConverter.Int64BitsToDouble(tmp << 32);
}
Wird der Ausdruck mit IEEE-floating-point "tricks" und ist hauptsächlich für die Verwendung in neuronalen sets. Die Funktion ist etwa 5-mal schneller als die normalen Math.Exp(x)
Funktion.
Leider, ist die numerische Genauigkeit ist nur -4% -- +2% gegenüber dem regulären Math.Exp(x)
Funktion, idealerweise würde ich gerne eine Genauigkeit von innerhalb mindestens der sub-Prozent-Bereich.
Habe ich gezeichnet, der quotient zwischen dem ungefähren und dem regulären Exp Funktionen, und wie gesehen werden kann, in der Grafik die relative Differenz scheint wiederholt zu werden, mit praktisch konstanter Frequenz.
Ist es möglich, dies zu nutzen Regelmäßigkeit zur Verbesserung der Genauigkeit der "schnelle exp" - Funktion weiter, ohne erhebliche Verringerung der Geschwindigkeit der Berechnung, oder würde der rechnerische Aufwand eine Genauigkeit Verbesserung überwiegen die rechnerische Verstärkung des ursprünglichen Ausdrucks?
(Wie sehen a side note, ich habe auch versucht, eine alternative Ansätzen, die in der gleichen Frage ALSO, aber mit diesem Ansatz nicht zu sein scheinen sehr effizient in C#, zumindest nicht für den Allgemeinen Fall.)
UPDATE MAI 14
Auf Wunsch von @Adriano, ich habe mir nun vorgenommen einen sehr einfachen Maßstab. Habe ich durchgeführt 10 Millionen Berechnungen unter Verwendung jeder der alternativen exp Funktionen für Gleitkomma-Werte im Bereich [-100, 100]. Da der Bereich der Werte, die ich bin daran interessiert, es erstreckt sich von -20 bis 0 habe ich auch explizit aufgeführt ist der Wert für die Funktion an der Stelle x = -5. Hier sind die Ergebnisse:
Math.Exp: 62.525 ms, exp(-5) = 0.00673794699908547
Empty function: 13.769 ms
ExpNeural: 14.867 ms, exp(-5) = 0.00675211846828461
ExpSeries8: 15.121 ms, exp(-5) = 0.00641270968867667
ExpSeries16: 32.046 ms, exp(-5) = 0.00673666189488182
exp1: 15.062 ms, exp(-5) = -12.3333325982094
exp2: 15.090 ms, exp(-5) = 13.708332516253
exp3: 16.251 ms, exp(-5) = -12.3333325982094
exp4: 17.924 ms, exp(-5) = 728.368055056781
exp5: 20.972 ms, exp(-5) = -6.13293614238501
exp6: 24.212 ms, exp(-5) = 3.55518353166184
exp7: 29.092 ms, exp(-5) = -1.8271053775984
exp7 +/-: 38.482 ms, exp(-5) = 0.00695945286970704
ExpNeural entspricht der Exp Funktion angegeben, die am Anfang dieses Textes. ExpSeries8 wird die Formulierung, dass ich ursprünglich behauptete, war nicht sehr effizient auf .NETTO; bei der Umsetzung ist es genau so wie Neil es war tatsächlich sehr schnell. ExpSeries16 wird die analoge Formel, aber mit 16 Multiplikationen statt 8. exp1 durch exp7 sind die verschiedenen Funktionen von Adriano ' s Antwort weiter unten. Die endgültige Variante des exp7 ist eine Variante, wo die Zeichen der x wird überprüft; wenn negativ, liefert die Funktion 1/exp(-x)
statt.
Leider weder von der expN Funktionen aufgeführt, die durch Adriano sind ausreichend in der breiteren negativen Wertebereich überlege ich. Die Serie Erweiterung Ansatz von Neil Coffey scheint zu sein, mehr geeignet, "meine" Wert-Bereich, obwohl es zu stark erweiternde, mit größeren negativen x, vor allem, wenn Sie mit "nur" 8 Multiplikationen.
- ich bin neugierig auf Ihre Referenz zu "neural-Sätzen". derzeit bin ich die Simulation eines neuronalen Netzes mit C++ und vor dem gleichen
exp
performance-Engpass, die Sie haben, konfrontiert. gibt es papers in computational neuroscience, die haben vorgeschlagen Ungefähreexp
Funktionen, die sind sehr schnell?
Du musst angemeldet sein, um einen Kommentar abzugeben.
Im Falle jemand will, um zu replizieren, die den relativen Fehler der Funktion angezeigt, in der Frage, hier ist ein Weg, mit Hilfe von Matlab (das "fast" exponent ist nicht sehr schnell in Matlab, aber es ist zutreffend):
Nun, die Zeit der Fehler genau mit dem übereinstimmt, wenn der binäre Wert des
tmp
überläufe aus der Mantisse in den Exponenten. Wir brechen unsere Daten in den Behältern durch das verwerfen der bits, werden die Exponenten (und damit periodische), und hält nur die hohen verbleibenden acht bits (um unsere lookup-Tabelle in einer vernünftigen Größe):Nun berechnen wir die mittlere erforderliche Anpassung:
Der relative Fehler verringert sich auf +/- .0006. Natürlich, anderen Größen Tabellen möglich (zum Beispiel 6-bit-Tabelle mit 64 Einträgen gibt +/- .0025) und der Fehler ist fast linear in der Tabelle size. Lineare interpolation zwischen die Einträge der Tabelle verbessern würde die Fehler noch weiter, aber auf Kosten der performance. Da wir bereits begegnet der Genauigkeit Ziel, lassen Sie uns vermeiden Sie jegliche weitere Leistung trifft.
Zu diesem Zeitpunkt ist es einigen trivial-editor-Fähigkeiten, um die berechneten Werte von MatLab und erstellen Sie eine lookup-Tabelle in C#. Für jede Berechnung, fügen wir eine Bitmaske, lookup-Tabelle, und mit doppelter Genauigkeit multipliziert.
Den speedup ist sehr ähnlich wie das original-code -- für meinen computer, das ist etwa 30% schneller als x86 kompiliert und etwa 3x so schnell für x64. Mit mono auf ideone, es ist eine erhebliche Netto-Verlust (aber so ist das original).
Kompletten source-code-und Testfall: http://ideone.com/UwNgx
memcpy
für Ihre Art-Zweideutigkeiten. Sowieso, je nachdem, ob Ihr Ziel hat sich floating-point-hardware, möchten Sie vielleicht, um die Verwendung von single-precision für die lookup-Tabelle. Wir reden hier von einem relativen Fehler von .0006, also mit doppelter Genauigkeit ist nicht zu helfen.BitConverter
Funktionen durch einememcpy
, und bewegen Sie den[]
in der array-definition. Der rest der C# - code ist gültig C bereits.Versuchen Sie folgenden alternativen (
exp1
ist schneller,exp7
mehr genau).Code
Präzision
Credits
Diese Implementierungen
exp()
wurden berechnet, indem die "scoofy" mit Taylor-Reihe von einemtanh()
Umsetzung von "fuzzpilz" (wer auch immer Sie sind, ich hatte nur diese Verweise auf meinen code).Taylor series approximation (wie die
expX()
Funktionen in Adriano ' s Antwort) die am genauesten sind nahe null und können erhebliche Fehler bei -20 oder sogar -5. Wenn die Eingabe einer bekannten Produktpalette, wie z.B. -20 0 wie die ursprüngliche Frage, die Sie verwenden können, eine kleine look-up-Tabelle und eine zusätzliche multiplizieren, um die Genauigkeit erheblich verbessern.Der trick ist zu erkennen, dass exp() getrennt werden kann, in integer und Bruch-Teile. Zum Beispiel:
Den Bruchteil wird immer zwischen -1 und 1, also a Taylor series approximation wird ziemlich genau. Der ganzzahlige Teil hat nur 21 mögliche Werte für exp(-20) exp(0), so können diese gespeichert werden, in einer kleinen look-up-Tabelle.
Sollte der folgende code-Adresse der Anforderungen an die Genauigkeit, wie für die Eingänge in [-87,88] die Ergebnisse sind als relative Fehler <= 1.73 e-3. Ich weiß nicht, C#, also das ist C-code, aber die Konvertierung sollte failry einfach.
Ich gehe davon aus, dass da die Genauigkeit niedrig ist, die Verwendung von single-precision-Berechnung in Ordnung ist. Ein klassischer Algorithmus verwendet wird, in dem die Berechnung von exp() zugeordnet ist Berechnung der bsp2(). Nach dem argument-Konvertierung per Multiplikation von log2(e), exponentation, indem Sie die Nachkommastellen werden durch einen minimax-Polynom von Grad 2, während die exponentation von den ganzzahligen Teil des Arguments erfolgt durch direkte manipulation der exponent Teil des IEEE-754-single-precision-Zahl.
Den flüchtigen union ermöglicht die re-interpretation eines bit-Muster wird entweder eine ganze Zahl oder eine single-precision-floating-point-Zahl, die benötigt werden für den Exponenten manipulation. Es sieht aus wie C# bietet decidated re-interpretation von Funktionen für diese, die ist viel sauberer.
Den zwei potentielle performance-Probleme sind die floor () - Funktion und float->int Umwandlung. Traditionell wurden beide langsam auf x86-aufgrund der Notwendigkeit der Bearbeitung dynamischer Prozessor Staat. Aber SSE (insbesondere SSE 4.1) enthält Anweisungen, die es ermöglichen, diese Vorgänge schnell sein. Ich weiß nicht ob die C# können machen, verwenden Sie diese Anweisungen.
memcpy
in C und C++, und der Optimierer sollte tun, etwas sinnvolles, ohne daß es bricht mit Optimierungen basierend auf strengen aliasing.__m128
)? Danke.expf()
SIMD-Implementierung und ich könnte dann auch beantwortet.Ich studiert haben, die Papier von Nicol Schraudolph, wo die originalen C-Implementierung der obigen Funktion definiert wurde, näher jetzt. Es scheint, dass es wahrscheinlich nicht möglich deutlich zu genehmigen, die Genauigkeit der exp Berechnung ohne erheblich beeinträchtigen die Leistung. Auf der anderen Seite, die Näherung ist gültig, auch für große Größen von x, bis zu +/- 700, die ist natürlich von Vorteil.
Die Implementierung der Funktion oben eingestellt ist, erhalten mindestens die Wurzel aus dem mittleren quadratischen Fehler. Schraudolph beschreibt, wie sich der additive term in der tmp Ausdruck kann verändert werden, um zu erreichen, alternative approximation Eigenschaften.
Er weist auch darauf hin, dass bei einer "mikroskopischen" Ebene der Ungefähre "exp" - Funktion weist stair-case-Verhalten seit 32 bits werden verworfen, bei der Umwandlung von lange zu Doppel -. Dies bedeutet, dass die Funktion ist stückweise konstant auf einem sehr kleinen Maßstab, aber die Funktion ist zumindest nie sinkt mit zunehmendem x.