Je voudrais calculer à la fois le sinus et le cosinus d'une valeur ensemble (par exemple pour créer une matrice de rotation). Bien sûr, je pourrais les calculer séparément l'un après l'autre a = cos(x); b = sin(x);
, mais je me demande s'il existe un moyen plus rapide lorsque vous avez besoin des deux valeurs.
Edit: Pour résumer les réponses à ce jour:
Vlad a dit qu'il y avait la commande asm qui calculait
FSINCOS
les deux (presque en même temps qu'un appel àFSIN
seul)Comme Chi l'a remarqué, cette optimisation est parfois déjà effectuée par le compilateur (lors de l'utilisation des indicateurs d'optimisation).
caf a souligné que les fonctions
sincos
etsincosf
sont probablement disponibles et peuvent être appelées directement en incluant simplementmath.h
l' approche tanascius consistant à utiliser une table de consultation est controversée. (Cependant, sur mon ordinateur et dans un scénario de référence, il fonctionne 3 fois plus vite
sincos
qu'avec presque la même précision pour les virgules flottantes 32 bits.)Joel Goodwin lié à une approche intéressante d'une technique d'approximation extrêmement rapide avec une assez bonne précision (pour moi, c'est encore plus rapide que la recherche de table)
sinx ~ x-x^3/6
etcosx~1-x^2/4
comme approximations si vous vous souciez de la vitesse plus que de la précision. Vous pouvez ajouter des termes dans l'une ou l'autre série au fur et à mesure que vous accordez plus de poids à la précision ( en.wikipedia.org/wiki/Taylor_series faites défiler vers le bas jusqu'à la série de trig taylor.) Notez qu'il s'agit d'une façon générale d'approximer toute fonction que vous voulez qui est différenten
. Donc, si vous avez une fonction plus grande à laquelle appartiennent ce sinus et ce cosinus, vous obtiendrez une vitesse beaucoup plus grande si vous l'approximez au lieu du sin, cos est indépendamment.x
proches d'un certain pointx_0
, puis élargissez votre série Taylorx_0
au lieu de 0. Cela vous donnera une excellente précision de prèsx_0
mais plus vous serez éloigné. pire les résultats. Vous avez probablement pensé que la précision était nulle en regardant la réponse donnée et en l'essayant pour des valeurs éloignées0
. Cette réponse est avec sin, cos augmenté autour de 0.Réponses:
Les processeurs Intel / AMD modernes ont des instructions
FSINCOS
pour calculer simultanément les fonctions sinus et cosinus. Si vous avez besoin d'une optimisation forte, vous devriez peut-être l'utiliser.Voici un petit exemple: http://home.broadpark.no/~alein/fsincos.html
Voici un autre exemple (pour MSVC): http://www.codeguru.com/forum/showthread.php?t=328669
Voici encore un autre exemple (avec gcc): http://www.allegro.cc/forums/thread/588470
J'espère que l'un d'eux aidera. (Je n'ai pas utilisé cette instruction moi-même, désolé.)
Comme ils sont pris en charge au niveau du processeur, je m'attends à ce qu'ils soient beaucoup plus rapides que les recherches de table.
Edit:
Wikipedia suggère qu'il a
FSINCOS
été ajouté à 387 processeurs, vous pouvez donc difficilement trouver un processeur qui ne le prend pas en charge.Edit:
la documentation d'Intel indique que
FSINCOS
c'est à peu près 5 fois plus lent queFDIV
(c'est-à-dire la division en virgule flottante).Edit:
Veuillez noter que tous les compilateurs modernes n'optimisent pas le calcul du sinus et du cosinus dans un appel à
FSINCOS
. En particulier, mon VS 2008 ne l'a pas fait de cette façon.Edit:
Le premier exemple de lien est mort, mais il existe toujours une version sur la Wayback Machine .
la source
fsincos
instruction n'est pas "assez rapide". Le manuel d'optimisation d'Intel le cite comme nécessitant entre 119 et 250 cycles sur des micro-architectures récentes. La bibliothèque mathématique d'Intel (distribuée avec ICC), par comparaison, peut calculer séparémentsin
etcos
en moins de 100 cycles, en utilisant une implémentation logicielle qui utilise SSE au lieu de l'unité x87. Une implémentation logicielle similaire qui calculait les deux simultanément pourrait être encore plus rapide.sin
Cependant, je peux vous dire qu'il n'y a pas de calcul intégré dont ils peuvent profiter; ils utilisent les mêmes instructions SSE que tout le monde. Pour votre deuxième commentaire, la vitesse par rapport àfdiv
est sans importance; s'il y a deux façons de faire quelque chose et que l'une est deux fois plus rapide que l'autre, cela n'a pas de sens d'appeler la plus lente «rapide», quel que soit le temps que cela prend par rapport à une tâche totalement indépendante.sin
fonction logicielle de leur bibliothèque offre une précision double précision totale. L'fsincos
instruction offre un peu plus de précision (double extension), mais cette précision supplémentaire est gâchée dans la plupart des programmes qui appellent lasin
fonction, car son résultat est généralement arrondi à la double précision par des opérations arithmétiques ultérieures ou un stockage en mémoire. Dans la plupart des situations, ils offrent la même précision pour une utilisation pratique.fsincos
n'est pas une implémentation complète en soi; vous avez besoin d'une étape de réduction de plage supplémentaire pour placer l'argument dans la plage d'entrée valide pour l'fsincos
instruction. La bibliothèquesin
et lescos
fonctions incluent cette réduction ainsi que le calcul de base, donc elles sont encore plus rapides (par comparaison) que les temps de cycle que j'ai énumérés pourraient indiquer.Les processeurs x86 modernes ont une instruction fsincos qui fera exactement ce que vous demandez - calculer sin et cos en même temps. Un bon compilateur d'optimisation devrait détecter le code qui calcule sin et cos pour la même valeur et utiliser la commande fsincos pour l'exécuter.
Il a fallu quelques changements d'indicateurs du compilateur pour que cela fonctionne, mais:
Tada, il utilise l'instruction fsincos!
la source
-ffast-math
et-mfpmath
conduisent à des résultats différents dans certains cas.fsin
etfcos
. :-(__CIsin
et__CIcos
.Lorsque vous avez besoin de performances, vous pouvez utiliser une table sin / cos précalculée (une table fera l'affaire, stockée sous forme de dictionnaire). Eh bien, cela dépend de la précision dont vous avez besoin (peut-être que la table serait trop grande), mais cela devrait être très rapide.
la source
sin
car la table précalculée mettra à la poubelle le cache.Techniquement, vous y parviendrez en utilisant des nombres complexes et la formule d'Euler . Ainsi, quelque chose comme (C ++)
devrait vous donner sinus et cosinus en une seule étape. Comment cela est fait en interne est une question du compilateur et de la bibliothèque utilisés. Cela pourrait (et pourrait) bien prendre plus de temps pour le faire de cette façon (simplement parce que la formule d'Euler est principalement utilisée pour calculer le complexe en
exp
utilisantsin
etcos
- et non l'inverse) mais il pourrait y avoir une optimisation théorique possible.Éditer
Les en-têtes
<complex>
de GNU C ++ 4.2 utilisent des calculs explicites desin
et à l'cos
intérieurpolar
, donc cela ne semble pas trop bon pour les optimisations à moins que le compilateur ne fasse un peu de magie (voir les commutateurs-ffast-math
et-mfpmath
comme écrit dans la réponse de Chi ).la source
Vous pouvez calculer l'un ou l'autre, puis utiliser l'identité:
mais comme le dit @tanascius, une table précalculée est la voie à suivre.
la source
sqrt()
est souvent optimisé dans le matériel, donc il peut très bien être plus rapide alorssin()
oucos()
. Le pouvoir est juste une auto-multiplication, alors ne l'utilisez paspow()
. Il existe quelques astuces pour obtenir très rapidement des racines carrées raisonnablement précises sans support matériel. Enfin, assurez-vous de profiler avant de faire quoi que ce soit.Si vous utilisez la bibliothèque GNU C, vous pouvez faire:
et vous obtiendrez des déclarations des fonctions
sincos()
,sincosf()
etsincosl()
qui calculent les deux valeurs ensemble - probablement de la manière la plus rapide pour votre architecture cible.la source
Il y a des choses très intéressantes sur cette page du forum, qui se concentre sur la recherche de bonnes approximations rapides: http://www.devmaster.net/forums/showthread.php?t=5784
Avis de non-responsabilité: Je n'ai utilisé aucun de ces produits moi-même
Mise à jour du 22 février 2018: Wayback Machine est le seul moyen de visiter la page d'origine maintenant: https://web.archive.org/web/20130927121234/http://devmaster.net/posts/9648/fast-and-accurate- sinus-cosinus
la source
De nombreuses bibliothèques de mathématiques C, comme l'indique caf, ont déjà sincos (). L'exception notable est MSVC.
Et concernant la recherche, Eric S. Raymond dans l' Art de la programmation Unix (2004) (chapitre 12) dit explicitement que c'est une mauvaise idée (à l'heure actuelle):
Mais, à en juger par la discussion ci-dessus, tout le monde n'est pas d'accord.
la source
fsincos
j'essaierais (instruction CPU!) Pour les autres. C'est souvent aussi rapide que d'interpoler sin et cos à partir d'une grande table.Je ne pense pas que les tables de consultation soient nécessairement une bonne idée pour ce problème. À moins que vos exigences de précision ne soient très faibles, la table doit être très grande. Et les processeurs modernes peuvent faire beaucoup de calculs pendant qu'une valeur est extraite de la mémoire principale. Ce n'est pas une de ces questions auxquelles on peut répondre correctement par un argument (pas même le mien), tester et mesurer et examiner les données.
Mais je regarderais les implémentations rapides de SinCos que vous trouvez dans des bibliothèques telles que ACML d'AMD et MKL d'Intel.
la source
Si vous êtes prêt à utiliser un produit commercial et que vous calculez un certain nombre de calculs sin / cos en même temps (afin que vous puissiez utiliser des fonctions vectorisées), vous devriez consulter la bibliothèque du noyau mathématique d'Intel.
Il a une fonction sincos
Selon cette documentation, il fait en moyenne 13,08 horloges / élément sur le duo core 2 en mode haute précision, ce qui, je pense, sera encore plus rapide que fsincos.
la source
vvsincos
ouvvsincosf
depuis le Accelerate.framework. Je pense qu'AMD a également des fonctions similaires dans leur bibliothèque vectorielle.Cet article montre comment construire un algorithme parabolique qui génère à la fois le sinus et le cosinus:
Astuce DSP: Approximation parabolique simultanée de Sin et Cos
http://www.dspguru.com/dsp/tricks/parabolic-approximation-of-sin-and-cos
la source
Lorsque les performances sont essentielles pour ce genre de choses, il n'est pas rare d'introduire une table de consultation.
la source
Pour une approche créative, que diriez-vous d'élargir la série Taylor? Puisqu'ils ont des termes similaires, vous pouvez faire quelque chose comme le pseudo suivant:
Cela signifie que vous faites quelque chose comme ceci: en commençant à x et 1 pour sin et cosinus, suivez le modèle - soustrayez x ^ 2/2! du cosinus, soustrayez x ^ 3/3! à partir de sinus, ajoutez x ^ 4/4! au cosinus, ajoutez x ^ 5/5! à sinus ...
Je ne sais pas si ce serait performant. Si vous avez besoin de moins de précision que les valeurs intégrées de sin () et cos (), cela peut être une option.
la source
Il existe une solution intéressante dans la bibliothèque CEPHES qui peut être assez rapide et vous pouvez ajouter / supprimer de la précision de manière assez flexible pour un peu plus / moins de temps CPU.
Rappelez-vous que cos (x) et sin (x) sont les parties réelle et imaginaire de exp (ix). Nous voulons donc calculer exp (ix) pour obtenir les deux. Nous précalculons exp (iy) pour certaines valeurs discrètes de y comprises entre 0 et 2pi. Nous décalons x à l'intervalle [0, 2pi). Ensuite, nous sélectionnons le y qui est le plus proche de x et écrivons
exp (ix) = exp (iy + (ix-iy)) = exp (iy) exp (i (xy)).
Nous obtenons exp (iy) de la table de recherche. Et puisque | xy | est petit (au plus la moitié de la distance entre les valeurs y), la série de Taylor convergera bien en quelques termes seulement, nous utilisons donc cela pour exp (i (xy)). Et puis nous avons juste besoin d'une multiplication complexe pour obtenir exp (ix).
Une autre propriété intéressante de ceci est que vous pouvez le vectoriser en utilisant SSE.
la source
Vous pouvez jeter un œil à http://gruntthepeon.free.fr/ssemath/ , qui propose une implémentation vectorisée SSE inspirée de la bibliothèque CEPHES. Il a une bonne précision (écart maximal de sin / cos de l'ordre de 5e-8) et de la vitesse (surpasse légèrement fsincos sur une base d'appel unique, et un gagnant clair sur plusieurs valeurs).
la source
J'ai publié une solution impliquant un assemblage ARM en ligne capable de calculer à la fois le sinus et le cosinus de deux angles à la fois ici: Sinus / cosinus rapide pour ARMv7 + NEON
la source
Une approximation précise mais rapide des fonctions sin et cos simultanément, en javascript, peut être trouvée ici: http://danisraelmalta.github.io/Fmath/ (facilement importé vers c / c ++)
la source
Avez-vous pensé à déclarer des tables de recherche pour les deux fonctions? Vous auriez encore à "calculer" sin (x) et cos (x), mais ce serait décidément plus rapide, si vous n'avez pas besoin d'un haut degré de précision.
la source
Le compilateur MSVC peut utiliser les fonctions SSE2 (internes)
dans les versions optimisées si les indicateurs de compilateur appropriés sont spécifiés (au minimum / O2 / arch: SSE2 / fp: fast). Les noms de ces fonctions semblent impliquer qu'elles ne calculent pas séparément sin et cos, mais les deux "en une seule étape".
Par exemple:
Assemblage (pour x86) avec / fp: rapide:
L'assembly (pour x86) sans / fp: fast mais avec / fp: precise à la place (qui est la valeur par défaut) appelle sin et cos séparés:
Donc / fp: fast est obligatoire pour l'optimisation sincos.
Mais veuillez noter que
n'est peut-être pas aussi précis que
en raison du "précis" manquant à la fin de son nom.
Sur mon système "légèrement" plus ancien (Intel Core 2 Duo E6750) avec le dernier compilateur MSVC 2019 et les optimisations appropriées, mon point de repère montre que l'appel sincos est environ 2,4 fois plus rapide que les appels séparés sin et cos.
la source