Ich möchte sowohl den Sinus als auch den Co-Sinus eines Wertes zusammen berechnen (zum Beispiel um eine Rotationsmatrix zu erstellen). Natürlich könnte ich sie wie nacheinander separat berechnen a = cos(x); b = sin(x);
, aber ich frage mich, ob es einen schnelleren Weg gibt, wenn beide Werte benötigt werden.
Bearbeiten: Um die bisherigen Antworten zusammenzufassen:
Vlad sagte, dass es den Befehl asm gibt, der
FSINCOS
beide berechnet (fast zur gleichen Zeit wie ein Anruf anFSIN
allein).Wie Chi bemerkt hat, wird diese Optimierung manchmal bereits vom Compiler durchgeführt (bei Verwendung von Optimierungsflags).
caf wies darauf hin, dass funktionen
sincos
undsincosf
wahrscheinlich verfügbar sind und direkt durch einfaches einschließen aufgerufen werden könnenmath.h
Der Tanascius- Ansatz zur Verwendung einer Nachschlagetabelle wird kontrovers diskutiert. (Auf meinem Computer und in einem Benchmark-Szenario läuft es jedoch dreimal schneller als
sincos
mit fast der gleichen Genauigkeit für 32-Bit-Gleitkommazahlen.)Joel Goodwin verband sich mit einem interessanten Ansatz einer extrem schnellen Approximationstechnik mit ziemlich guter Genauigkeit (für mich ist dies sogar schneller als das Nachschlagen der Tabelle).
sinx ~ x-x^3/6
undcosx~1-x^2/4
als Annäherungen , wenn Sie kümmern sich um Geschwindigkeit mehr als Genauigkeit. Sie können Begriffe in beiden Serien hinzufügen, wenn Sie der Genauigkeit mehr Gewicht beimessen ( en.wikipedia.org/wiki/Taylor_series scrollen Sie nach unten, um Taylor-Serien auszulösen.) Beachten Sie, dass dies eine allgemeine Methode ist, um jede gewünschte Funktion zu approximieren, die unterschiedlichen
Zeiten aufweist. Wenn Sie also eine größere Funktion haben, zu der Sinus und Cosinus gehören, werden Sie eine viel größere Geschwindigkeit erhalten, wenn Sie sie anstelle der Sünde approximieren, cos unabhängig.x
nahe an einem bestimmten Punkt benötigen.x_0
Erweitern Sie dann Ihre Taylor-Reihe umx_0
statt 0. Dies gibt Ihnen eine hervorragende Genauigkeit in der Nähe,x_0
aber je weiter Sie entfernt sind Je schlechter die Ergebnisse. Sie haben wahrscheinlich gedacht, dass die Genauigkeit schlecht ist, als Sie sich die gegebene Antwort angesehen und sie für Werte ausprobiert haben, die weit davon entfernt sind0
. Diese Antwort ist mit Sünde, weil um 0 erweitert.Antworten:
Moderne Intel / AMD-Prozessoren verfügen über Anweisungen
FSINCOS
zur gleichzeitigen Berechnung von Sinus- und Cosinusfunktionen. Wenn Sie eine starke Optimierung benötigen, sollten Sie diese möglicherweise verwenden.Hier ist ein kleines Beispiel: http://home.broadpark.no/~alein/fsincos.html
Hier ist ein weiteres Beispiel (für MSVC): http://www.codeguru.com/forum/showthread.php?t=328669
Hier ist noch ein weiteres Beispiel (mit gcc): http://www.allegro.cc/forums/thread/588470
Hoffe einer von ihnen hilft. (Ich habe diese Anweisung leider nicht selbst verwendet.)
Da sie auf Prozessorebene unterstützt werden, erwarte ich, dass sie viel schneller sind als Tabellensuchen.
Bearbeiten:
Wikipedia schlägt vor, dass
FSINCOS
bei 387 Prozessoren hinzugefügt wurde, so dass Sie kaum einen Prozessor finden können, der dies nicht unterstützt.Bearbeiten:
Intels Dokumentation besagt, dass dies
FSINCOS
nur etwa fünfmal langsamer ist alsFDIV
(dh Gleitkommadivision).Bearbeiten:
Bitte beachten Sie, dass nicht alle modernen Compiler die Berechnung von Sinus und Cosinus in einen Aufruf von optimieren
FSINCOS
. Insbesondere mein VS 2008 hat es nicht so gemacht.Bearbeiten:
Der erste Beispiellink ist tot, aber es gibt noch eine Version auf der Wayback-Maschine .
quelle
fsincos
Anweisung ist nicht "ziemlich schnell". In Intels eigenem Optimierungshandbuch wird angegeben, dass bei neueren Mikroarchitekturen zwischen 119 und 250 Zyklen erforderlich sind. Die mit ICC vertriebene Mathematikbibliothek von Intel kann im Vergleich dazu separatsin
undcos
in weniger als 100 Zyklen mithilfe einer Softwareimplementierung berechnet werden , die SSE anstelle der x87-Einheit verwendet. Eine ähnliche Software-Implementierung, die beide gleichzeitig berechnet, könnte noch schneller sein.sin
Berechnung gibt, die sie nutzen könnten. Sie verwenden dieselben SSE-Anweisungen wie alle anderen. Für Ihren zweiten Kommentar ist die Geschwindigkeit relativ zufdiv
unerheblich. Wenn es zwei Möglichkeiten gibt, etwas zu tun, und eine doppelt so schnell ist wie die andere, ist es nicht sinnvoll, die langsamere als "schnell" zu bezeichnen, unabhängig davon, wie lange es im Vergleich zu einer völlig unabhängigen Aufgabe dauert.sin
in ihrer Bibliothek bietet volle Genauigkeit mit doppelter Genauigkeit. Derfsincos
Befehl liefert etwas mehr Genauigkeit (doppelt erweitert), aber diese zusätzliche Genauigkeit wird in den meisten Programmen, die diesin
Funktion aufrufen , weggeworfen , da das Ergebnis normalerweise durch spätere arithmetische Operationen oder durch Speichern im Speicher auf doppelte Genauigkeit gerundet wird. In den meisten Situationen liefern sie für den praktischen Gebrauch die gleiche Genauigkeit.fsincos
keine vollständige Implementierung für sich ist. Sie benötigen einen zusätzlichen Schritt zur Bereichsreduzierung, um das Argument in den gültigen Eingabebereich für diefsincos
Anweisung zu setzen. Die Bibliotheksin
und diecos
Funktionen umfassen diese Reduzierung sowie die Kernberechnung, sodass sie (im Vergleich) noch schneller sind als die von mir aufgelisteten Zykluszeiten.Moderne x86-Prozessoren verfügen über eine fsincos-Anweisung, die genau das tut, was Sie verlangen - berechnen Sie gleichzeitig sin und cos. Ein guter Optimierungscompiler sollte Code erkennen, der sin und cos für denselben Wert berechnet, und den Befehl fsincos verwenden, um dies auszuführen.
Es dauerte ein wenig, bis die Compiler-Flags funktionierten, aber:
Tada, es benutzt die Anweisung fsincos!
quelle
-ffast-math
und-mfpmath
führen in einigen Fällen zu unterschiedlichen Ergebnissen.fsin
undfcos
. :-(__CIsin
und__CIcos
.Wenn Sie Leistung benötigen, können Sie eine vorberechnete sin / cos-Tabelle verwenden (eine Tabelle reicht aus, die als Wörterbuch gespeichert ist). Nun, es hängt von der Genauigkeit ab, die Sie benötigen (vielleicht wäre der Tisch zu groß), aber es sollte wirklich schnell gehen.
quelle
sin
da die vorberechnete Tabelle den Cache in den Papierkorb legt.Technisch gesehen würden Sie dies erreichen, indem Sie komplexe Zahlen und die Euler-Formel verwenden . Also so etwas wie (C ++)
sollte Ihnen Sinus und Cosinus in einem Schritt geben. Wie dies intern erfolgt, hängt vom verwendeten Compiler und der verwendeten Bibliothek ab. Es könnte (und könnte) durchaus länger dauern, dies auf diese Weise zu tun (nur weil Eulers Formel hauptsächlich zur Berechnung des Komplexes
exp
mitsin
und verwendet wirdcos
- und nicht umgekehrt), aber möglicherweise ist eine theoretische Optimierung möglich.Bearbeiten
Die Header in
<complex>
GNU C ++ 4.2 verwenden explizite Berechnungen vonsin
undcos
innerhalbpolar
, sodass es für Optimierungen dort nicht allzu gut aussieht, es sei denn, der Compiler zaubert etwas (siehe-ffast-math
und-mfpmath
wechselt, wie in Chis Antwort geschrieben ).quelle
Sie können entweder berechnen und dann die Identität verwenden:
Aber wie @tanascius sagt, ist eine vorberechnete Tabelle der richtige Weg.
quelle
sqrt()
wird oft in Hardware optimiert, so dass es sehr wohl schneller sein kann alssin()
odercos()
. Die Kraft ist nur Selbstmultiplikation, also nicht verwendenpow()
. Es gibt einige Tricks, um ohne Hardware-Unterstützung sehr schnell ziemlich genaue Quadratwurzeln zu erhalten. Stellen Sie zum Schluss sicher, dass Sie ein Profil haben, bevor Sie dies tun.Wenn Sie die GNU C-Bibliothek verwenden, können Sie Folgendes tun:
und Sie erhalten Erklärungen der
sincos()
,sincosf()
undsincosl()
Funktionen , die beide Werte zusammen berechnen - vermutlich auf dem schnellsten Weg für Ihre Zielarchitektur.quelle
Es gibt sehr interessante Dinge auf dieser Forenseite, die sich darauf konzentrieren, gute Annäherungen zu finden, die schnell sind: http://www.devmaster.net/forums/showthread.php?t=5784
Haftungsausschluss: Ich habe nichts davon selbst verwendet.
Update 22. Februar 2018: Wayback Machine ist die einzige Möglichkeit, die Originalseite jetzt zu besuchen: https://web.archive.org/web/20130927121234/http://devmaster.net/posts/9648/fast-and-accurate- Sinus-Cosinus
quelle
Viele C-Mathe-Bibliotheken haben, wie das Café angibt, bereits sincos (). Die bemerkenswerte Ausnahme ist MSVC.
Und in Bezug auf das Nachschlagen sagt Eric S. Raymond in der Kunst der Unix-Programmierung (2004) (Kapitel 12) ausdrücklich, dass dies eine schlechte Idee ist (zum gegenwärtigen Zeitpunkt):
Nach der obigen Diskussion sind sich jedoch nicht alle einig.
quelle
fsincos
(CPU-Anweisung!) Einen Versuch für die anderen geben. Es ist oft so schnell wie das Interpolieren von sin und cos von einem großen Tisch.Ich glaube nicht, dass Nachschlagetabellen unbedingt eine gute Idee für dieses Problem sind. Sofern Ihre Genauigkeitsanforderungen nicht sehr niedrig sind, muss die Tabelle sehr groß sein. Und moderne CPUs können viel rechnen, während ein Wert aus dem Hauptspeicher abgerufen wird. Dies ist keine dieser Fragen, die durch Argumente (nicht einmal meine) richtig beantwortet, getestet und gemessen und die Daten berücksichtigt werden können.
Aber ich würde mich auf die schnellen Implementierungen von SinCos konzentrieren, die Sie in Bibliotheken wie AMDs ACML und Intels MKL finden.
quelle
Wenn Sie bereit sind, ein kommerzielles Produkt zu verwenden und gleichzeitig eine Reihe von Sin / Cos-Berechnungen berechnen (damit Sie Vektorfunktionen verwenden können), sollten Sie sich die Math Kernel Library von Intel ansehen .
Es hat eine Sincos-Funktion
Laut dieser Dokumentation sind es durchschnittlich 13,08 Takte / Element auf Core 2 Duo im hochgenauen Modus, was meiner Meinung nach sogar noch schneller sein wird als fsincos.
quelle
vvsincos
odervvsincosf
aus dem Accelerate.framework verwenden. Ich glaube, dass AMD ähnliche Funktionen auch in seiner Vektorbibliothek hat.Dieser Artikel zeigt, wie ein parabolischer Algorithmus erstellt wird, der sowohl den Sinus als auch den Cosinus erzeugt:
DSP-Trick: Simultane parabolische Approximation von Sin und Cos
http://www.dspguru.com/dsp/tricks/parabolic-approximation-of-sin-and-cos
quelle
Wenn die Leistung für solche Dinge entscheidend ist, ist es nicht ungewöhnlich, eine Nachschlagetabelle einzuführen.
quelle
Wie wäre es mit einer Erweiterung der Taylor-Serie für einen kreativen Ansatz? Da sie ähnliche Begriffe haben, können Sie so etwas wie das folgende Pseudo machen:
Dies bedeutet, dass Sie so etwas tun: Beginnen Sie bei x und 1 für sin und cosine und folgen Sie dem Muster - subtrahieren Sie x ^ 2/2! vom Kosinus subtrahiere x ^ 3/3! addiere vom Sinus x ^ 4/4! zum Cosinus addiere x ^ 5/5! zu sinus ...
Ich habe keine Ahnung, ob dies performant wäre. Wenn Sie weniger Präzision benötigen als die eingebauten sin () und cos (), kann dies eine Option sein.
quelle
Es gibt eine nette Lösung in der CEPHES-Bibliothek, die ziemlich schnell sein kann und Sie können die Genauigkeit für etwas mehr / weniger CPU-Zeit ziemlich flexibel hinzufügen / entfernen.
Denken Sie daran, dass cos (x) und sin (x) die Real- und Imaginärteile von exp (ix) sind. Wir wollen also exp (ix) berechnen, um beides zu erhalten. Wir berechnen exp (iy) für einige diskrete Werte von y zwischen 0 und 2 pi vor. Wir verschieben x in das Intervall [0, 2pi]. Dann wählen wir das y aus, das x am nächsten liegt, und schreiben
exp (ix) = exp (iy + (ix-iy)) = exp (iy) exp (i (xy)).
Wir erhalten exp (iy) aus der Nachschlagetabelle. Und seit | xy | Ist die Taylor-Reihe klein (höchstens die Hälfte des Abstandes zwischen den y-Werten), konvergiert sie in wenigen Begriffen gut, daher verwenden wir sie für exp (i (xy)). Und dann brauchen wir nur eine komplexe Multiplikation, um exp (ix) zu erhalten.
Eine weitere nette Eigenschaft ist, dass Sie es mit SSE vektorisieren können.
quelle
Vielleicht möchten Sie einen Blick auf http://gruntthepeon.free.fr/ssemath/ werfen , das eine vektorisierte SSE-Implementierung bietet, die von der CEPHES-Bibliothek inspiriert ist. Es hat eine gute Genauigkeit (maximale Abweichung von sin / cos in der Größenordnung von 5e-8) und Geschwindigkeit (übertrifft fsincos bei einem einzelnen Anruf leicht und ist ein klarer Gewinner über mehrere Werte).
quelle
Ich habe hier eine Lösung veröffentlicht, die eine Inline-ARM-Baugruppe umfasst, mit der sowohl der Sinus als auch der Cosinus von zwei Winkeln gleichzeitig berechnet werden können: Schneller Sinus / Cosinus für ARMv7 + NEON
quelle
Eine genaue und dennoch schnelle Annäherung der sin- und cos-Funktion gleichzeitig in Javascript finden Sie hier: http://danisraelmalta.github.io/Fmath/ (leicht in c / c ++ importiert)
quelle
Haben Sie darüber nachgedacht, Nachschlagetabellen für die beiden Funktionen zu deklarieren? Sie müssten noch sin (x) und cos (x) "berechnen", aber es wäre deutlich schneller, wenn Sie kein hohes Maß an Genauigkeit benötigen.
quelle
Der MSVC-Compiler kann die (internen) SSE2-Funktionen verwenden
in optimierten Builds, wenn entsprechende Compiler-Flags angegeben sind (mindestens / O2 / arch: SSE2 / fp: fast). Die Namen dieser Funktionen scheinen zu implizieren, dass sie nicht getrennte Sünde und cos berechnen, sondern beide "in einem Schritt".
Beispielsweise:
Assembly (für x86) mit / fp: schnell:
Assembly (für x86) ohne / fp: schnell, aber mit / fp: präzise (was die Standardeinstellung ist) ruft separate sin und cos auf:
Also / fp: schnell ist für die sincos-Optimierung obligatorisch.
Aber bitte beachten Sie das
ist vielleicht nicht so genau wie
aufgrund des fehlenden "präzisen" am Ende seines Namens.
Auf meinem "etwas" älteren System (Intel Core 2 Duo E6750) mit dem neuesten MSVC 2019-Compiler und entsprechenden Optimierungen zeigt mein Benchmark, dass der Sincos-Aufruf etwa 2,4-mal schneller ist als separate Sin- und Cos-Aufrufe.
quelle