Chciałbym obliczyć zarówno sinus, jak i współsinus wartości razem (na przykład, aby utworzyć macierz rotacji). Oczywiście mógłbym je obliczyć osobno, jeden po drugim, npa = cos(x); b = sin(x);
, ale zastanawiam się, czy istnieje szybszy sposób, gdy potrzebuję obu wartości.
Edycja: podsumowanie dotychczasowych odpowiedzi:
Vlad powiedział, że istnieje polecenie asm
FSINCOS
obliczające oba z nich (prawie w tym samym czasie, co wywołanieFSIN
samego)Jak zauważył Chi , ta optymalizacja jest czasami już wykonywana przez kompilator (przy użyciu flag optymalizacji).
caf wskazał, że działa
sincos
isincosf
prawdopodobnie są dostępne i można je wywołać bezpośrednio, po prostu włączającmath.h
Podejście Tanasciusa do korzystania z tabeli przeglądowej jest dyskutowane jako kontrowersyjne. (Jednak na moim komputerze iw scenariuszu porównawczym działa 3x szybciej niż
sincos
z prawie taką samą dokładnością dla 32-bitowych liczb zmiennoprzecinkowych).Joel Goodwin połączył się z ciekawym podejściem do ekstremalnie szybkiej techniki przybliżania z całkiem dobrą dokładnością (dla mnie jest to nawet szybsze niż przeglądanie tabeli)
sinx ~ x-x^3/6
icosx~1-x^2/4
jako przybliżenia, jeśli bardziej zależy Ci na szybkości niż dokładności. Możesz dodawać terminy w obu seriach, kładąc większy nacisk na dokładność ( en.wikipedia.org/wiki/Taylor_series przewiń w dół do serii tryg taylor). Zauważ, że jest to ogólny sposób przybliżenia dowolnej funkcji, która ma różnen
czasy. Więc jeśli masz jakąś większą funkcję, do której należą te sinus i cosinus, uzyskasz znacznie większe przyspieszenie, jeśli przybliżasz ją zamiast sinusa, cos niezależnie.x
bliskich pewnego punktux_0
, a następnie rozszerz serię Taylora wokółx_0
zamiast 0. To da doskonałą dokładność w pobliżu,x_0
ale im dalej gorzej wyniki. Prawdopodobnie pomyślałeś, że dokładność jest do niczego, gdy spojrzałeś na podaną odpowiedź i wypróbowałeś ją dla wartości dalekich od0
. Odpowiedź brzmi: sin, cos rozszerzony wokół 0.Odpowiedzi:
Nowoczesne procesory Intel / AMD mają instrukcje
FSINCOS
do jednoczesnego obliczania funkcji sinus i cosinus. Jeśli potrzebujesz silnej optymalizacji, być może powinieneś jej użyć.Oto mały przykład: http://home.broadpark.no/~alein/fsincos.html
Oto kolejny przykład (dla MSVC): http://www.codeguru.com/forum/showthread.php?t=328669
Oto kolejny przykład (z gcc): http://www.allegro.cc/forums/thread/588470
Mam nadzieję, że jeden z nich pomoże. (Sam nie skorzystałem z tej instrukcji, przepraszam.)
Ponieważ są obsługiwane na poziomie procesora, spodziewam się, że będą znacznie szybsze niż przeszukiwanie tabeli.
Edycja:
Wikipedia to sugeruje
FSINCOS
została dodana na 387 procesorach, więc trudno jest znaleźć procesor, który go nie obsługuje.Edycja:
dokumentacja Intela stwierdza, że
FSINCOS
jest to tylko około 5 razy wolniejsze niżFDIV
(tj. Dzielenie zmiennoprzecinkowe).Edycja:
Należy pamiętać, że nie wszystkie współczesne kompilatory optymalizują obliczenia sinusa i cosinusa w wywołaniu funkcji
FSINCOS
. W szczególności mój VS 2008 nie robił tego w ten sposób.Edycja:
pierwszy link do przykładu jest martwy, ale nadal istnieje wersja w Wayback Machine .
źródło
fsincos
Instrukcja jest nie „dość szybko”. Według własnego podręcznika optymalizacji firmy Intel wymaga to od 119 do 250 cykli na najnowszych mikroarchitekturach. Biblioteka Intel Math (rozprowadzany z MTK), dla porównania, można oddzielnie obliczyćsin
icos
mniej niż 100 cykli, przy użyciu implementacji oprogramowania, który używa SSE zamiast jednostki x87. Podobna implementacja oprogramowania, która oblicza oba jednocześnie, mogłaby być jeszcze szybsza.sin
obliczeń, z których mogliby skorzystać; używają tych samych instrukcji SSE, co wszyscy inni. Odnośnie twojego drugiego komentarza, prędkość względemfdiv
jest nieistotna; jeśli istnieją dwa sposoby zrobienia czegoś, a jeden jest dwa razy szybszy od drugiego, nie ma sensu nazywać wolniejszego „szybkim”, niezależnie od tego, ile czasu zajmuje to w stosunku do jakiegoś zupełnie niezwiązanego zadania.sin
w ich bibliotece zapewnia pełną podwójną precyzję.fsincos
Instrukcja zapewnia nieco większą dokładność (dwukrotnie przedłużony), ale dodatkowo dokładność zostaje wyrzucone w większości programów, które nazywamysin
funkcję, a jego wynik jest zazwyczaj zaokrągla się do podwójnej precyzji przez później operacji arytmetycznych lub są zapisywane w pamięci. W większości sytuacji zapewniają taką samą dokładność w praktycznym zastosowaniu.fsincos
nie jest to pełna implementacja sama w sobie; potrzebujesz dodatkowego kroku redukcji zakresu, aby umieścić argument w poprawnym zakresie wejściowym dlafsincos
instrukcji. Bibliotekasin
icos
funkcje obejmują tę redukcję, a także podstawowe obliczenia, więc są one jeszcze szybsze (w porównaniu) niż wskazywane przeze mnie czasy cykli.Nowoczesne procesory x86 mają instrukcję fsincos, która zrobi dokładnie to, o co prosisz - oblicza sin i cos w tym samym czasie. Dobry kompilator optymalizujący powinien wykryć kod, który oblicza sin i cos dla tej samej wartości i użyć polecenia fsincos, aby to wykonać.
Aby to zadziałało, trzeba było trochę zmienić flagi kompilatora, ale:
Tada, używa instrukcji fsincos!
źródło
-ffast-math
i-mfpmath
w niektórych przypadkach prowadzi do różnych wyników.fsin
ifcos
. :-(__CIsin
i__CIcos
.Kiedy potrzebujesz wydajności, możesz użyć wstępnie obliczonej tabeli sin / cos (wystarczy jedna tabela, przechowywana jako słownik). Cóż, zależy to od potrzebnej dokładności (być może stół byłby za duży), ale powinien być naprawdę szybki.
źródło
sin
ponieważ wstępnie obliczona tabela usunie pamięć podręczną.Technicznie rzecz biorąc, osiągnąłbyś to używając liczb zespolonych i wzoru Eulera . Tak więc coś w stylu (C ++)
powinien dać ci sinus i cosinus w jednym kroku. Jak to się robi wewnętrznie, jest kwestią używanego kompilatora i biblioteki. Zrobienie tego w ten sposób mogłoby (i mogłoby) zająć więcej czasu (tylko dlatego, że formuła Eulera jest głównie używana do obliczania złożonego
exp
przy użyciusin
icos
- a nie na odwrót), ale może być możliwa pewna teoretyczna optymalizacja.Edytować
Nagłówki w
<complex>
GNU C ++ 4.2 używają jawnych obliczeńsin
icos
wewnątrzpolar
, więc nie wygląda to zbyt dobrze do optymalizacji, chyba że kompilator zrobi trochę magii (zobacz przełączniki-ffast-math
i-mfpmath
tak, jak napisano w odpowiedzi Chi ).źródło
Możesz obliczyć jedną z nich, a następnie użyć tożsamości:
ale jak mówi @tanascius, najlepszym rozwiązaniem jest tabela obliczona wcześniej.
źródło
sqrt()
jest często zoptymalizowany sprzętowo, więc może być wtedy bardzo szybszysin()
lubcos()
. Moc jest po prostu rozmnażaniem się, więc nie używajpow()
. Istnieje kilka sztuczek, które pozwalają szybko uzyskać dość dokładne pierwiastki kwadratowe bez wsparcia sprzętowego. Na koniec pamiętaj, aby utworzyć profil, zanim to zrobisz.Jeśli używasz biblioteki GNU C, możesz:
a dostaniesz oświadczeń o
sincos()
,sincosf()
isincosl()
funkcje, które obliczają obie wartości razem - przypuszczalnie w najszybszy sposób do architektury docelowej.źródło
Na tej stronie forum jest bardzo interesująca rzecz, która koncentruje się na wyszukiwaniu dobrych i szybkich przybliżeń: http://www.devmaster.net/forums/showthread.php?t=5784
Zastrzeżenie: Sam nie używałem żadnych z tych rzeczy.
Aktualizacja z 22 lutego 2018 r .: Wayback Machine to jedyny sposób, aby teraz odwiedzić oryginalną stronę: https://web.archive.org/web/20130927121234/http://devmaster.net/posts/9648/fast-and-accurate- sinus-cosinus
źródło
Wiele bibliotek matematycznych języka C, jak wskazuje caf, ma już sincos (). Godnym uwagi wyjątkiem jest MSVC.
A jeśli chodzi o wyszukiwanie, Eric S. Raymond w Art of Unix Programming (2004) (rozdział 12) mówi wyraźnie, że to zły pomysł (w chwili obecnej):
Ale sądząc po powyższej dyskusji, nie wszyscy się zgadzają.
źródło
fsincos
(instrukcje CPU!) Innym. Często jest tak szybkie, jak interpolacja sinusa i cos z dużego stołu.Nie wierzę, że tabele przeglądowe są koniecznie dobrym pomysłem na ten problem. O ile wymagania dotyczące dokładności nie są bardzo niskie, stół musi być bardzo duży. Nowoczesne procesory mogą wykonywać wiele obliczeń, podczas gdy wartość jest pobierana z pamięci głównej. Nie jest to jedno z tych pytań, na które można właściwie odpowiedzieć argumentacją (nawet moją), przetestować, zmierzyć i rozważyć dane.
Spojrzałbym jednak na szybkie implementacje SinCos, które można znaleźć w bibliotekach, takich jak ACML AMD i MKL Intela.
źródło
Jeśli chcesz korzystać z produktu komercyjnego i obliczasz jednocześnie kilka obliczeń sin / cos (abyś mógł używać funkcji wektorowych), powinieneś sprawdzić bibliotekę jąder matematycznych firmy Intel.
Posiada funkcję SinCos
Zgodnie z tą dokumentacją uśrednia on 13,08 zegarów / element na rdzeniu 2 duo w trybie wysokiej dokładności, co, jak sądzę, będzie nawet szybsze niż fsincos.
źródło
vvsincos
lubvvsincosf
z Accelerate.framework. Uważam, że AMD ma również podobne funkcje w swojej bibliotece wektorowej.W tym artykule pokazano, jak skonstruować algorytm paraboliczny, który generuje zarówno sinus, jak i cosinus:
DSP Trick: Równoczesne paraboliczne przybliżenie sinusa i cos
http://www.dspguru.com/dsp/tricks/parabolic-approximation-of-sin-and-cos
źródło
Gdy wydajność ma kluczowe znaczenie dla tego rodzaju rzeczy, wprowadzenie tabeli przeglądowej nie jest niczym niezwykłym.
źródło
Jeśli chodzi o kreatywne podejście, co powiesz na rozszerzenie serii Taylor? Ponieważ mają podobne terminy, możesz zrobić coś podobnego do następującego pseudo:
Oznacza to, że robisz coś takiego: zaczynając od x i 1 dla sin i cosinus, postępuj zgodnie ze wzorem - odejmij x ^ 2/2! od cosinusa odejmij x ^ 3/3! od sinusa dodaj x ^ 4/4! do cosinusa dodaj x ^ 5/5! sine ...
Nie mam pojęcia, czy to byłoby skuteczne. Jeśli potrzebujesz mniejszej precyzji niż dają ci wbudowane funkcje sin () i cos (), może to być opcja.
źródło
W bibliotece CEPHES jest fajne rozwiązanie, które może być dość szybkie i możesz dodawać / usuwać dokładność dość elastycznie, aby uzyskać nieco więcej / mniej czasu procesora.
Pamiętaj, że cos (x) i sin (x) to rzeczywiste i urojone części exp (ix). Chcemy więc obliczyć exp (ix), aby uzyskać oba. Obliczamy wstępnie exp (iy) dla niektórych dyskretnych wartości y między 0 a 2pi. Przesuwamy x do przedziału [0, 2pi). Następnie wybieramy y, które jest najbliższe x i piszemy
exp (ix) = exp (iy + (ix-iy)) = exp (iy) exp (i (xy)).
Otrzymujemy exp (iy) z tabeli przeglądowej. A ponieważ | xy | jest mała (co najwyżej połowa odległości między wartościami y), szereg Taylora będzie się dobrze zbiegał w zaledwie kilku terminach, więc używamy tego dla exp (i (xy)). A potem potrzebujemy złożonego mnożenia, aby otrzymać exp (ix).
Inną fajną właściwością tego jest to, że możesz go wektoryzować za pomocą SSE.
źródło
Możesz zajrzeć na http://gruntthepeon.free.fr/ssemath/ , który oferuje wektoryzowaną implementację SSE inspirowaną biblioteką CEPHES. Ma dobrą dokładność (maksymalne odchylenie od sin / cos rzędu 5e-8) i prędkość (nieznacznie przewyższa fsincos na podstawie pojedynczego wywołania i wyraźny zwycięzca w wielu wartościach).
źródło
Opublikowałem tutaj rozwiązanie obejmujące montaż inline ARM zdolny do obliczania zarówno sinusa, jak i cosinusa dwóch kątów naraz: Szybki sinus / cosinus dla ARMv7 + NEON
źródło
Dokładne, ale szybkie przybliżenie funkcji sin i cos jednocześnie, w javascript, można znaleźć tutaj: http://danisraelmalta.github.io/Fmath/ (łatwe importowanie do c / c ++)
źródło
Czy myślałeś o zadeklarowaniu tabel przeglądowych dla tych dwóch funkcji? Nadal musiałbyś „obliczyć” sin (x) i cos (x), ale byłoby to zdecydowanie szybsze, gdybyś nie potrzebował wysokiego stopnia dokładności.
źródło
Kompilator MSVC może używać (wewnętrznych) funkcji SSE2
w zoptymalizowanych kompilacjach, jeśli określono odpowiednie flagi kompilatora (co najmniej / O2 / arch: SSE2 / fp: fast). Nazwy tych funkcji zdają się sugerować, że nie obliczają one oddzielnych wartości sin i cos, ale obie „w jednym kroku”.
Na przykład:
Montaż (dla x86) z / fp: szybki:
Assembly (dla x86) bez / fp: fast, ale z / fp: precyzyjne zamiast tego (co jest domyślne) wywołuje oddzielne sin i cos:
So / fp: fast jest obowiązkowe dla optymalizacji sincos.
Ale proszę o tym pamiętać
może nie jest tak dokładny jak
ze względu na brak słowa „precyzyjne” na końcu jego nazwy.
Na moim "nieco" starszym systemie (Intel Core 2 Duo E6750) z najnowszym kompilatorem MSVC 2019 i odpowiednimi optymalizacjami mój test porównawczy pokazuje, że wywołanie sincos jest około 2,4 razy szybsze niż oddzielne wywołania sin i cos.
źródło