Quel est le moyen le plus rapide de calculer sin et cos ensemble?

Je voudrais calculer le sinus et le co-sinus d'une valeur (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 comme a = cos(x); b = sin(x);, mais je me demande s'il y a un moyen plus rapide lorsque vous avez besoin des deux valeurs.

Modifier: Pour résumer les réponses jusqu'à présent:

  • Vlad DIT, qu'il y a la commande asm FSINCOS calculant les deux (presque en même temps qu'un appel à FSIN seul)

  • Comme Chi remarqué, cette optimisation est parfois déjà fait par le compilateur (lors de l'utilisation de l'optimisation des drapeaux).

  • la caf souligné, que les fonctions de sincos et sincosf sont probablement disponibles et peuvent être appelés directement par juste, y compris math.h

  • tanascius approche de l'utilisation d'un tableau est discuté controversée. (Cependant sur mon ordinateur et dans un benchmark scénario il fonctionne 3 fois plus vite que sincos avec presque la même précision pour les points flottants 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)

92
demandé sur Community 2010-04-21 18:05:53

18 réponses

Les processeurs Intel/AMD modernes ont l'instruction FSINCOS pour calculer les fonctions sinus et cosinus simultanément. 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 vous 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.

Modifier:
Wikipedia suggère que FSINCOS a été ajouté à 387 processeurs, donc vous pouvez difficilement trouver un processeur qui ne le supporte pas.

Modifier:
la documentation D'Intel indique que FSINCOS est à peu près 5 fois plus lent que FDIV (c'est-à-dire, division en virgule flottante).

Modifier:
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.

Modifier:
Le premier exemple de lien est mort, mais il y a toujours une version sur la Machine Wayback .

48
répondu Vlad 2011-12-19 23:01:35

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 twiddling de drapeaux du compilateur pour que cela fonctionne, mais:

$ gcc --version
i686-apple-darwin9-gcc-4.0.1 (GCC) 4.0.1 (Apple Inc. build 5488)
Copyright (C) 2005 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ cat main.c
#include <math.h> 

struct Sin_cos {double sin; double cos;};

struct Sin_cos fsincos(double val) {
  struct Sin_cos r;
  r.sin = sin(val);
  r.cos = cos(val);
  return r;
}

$ gcc -c -S -O3 -ffast-math -mfpmath=387 main.c -o main.s

$ cat main.s
    .text
    .align 4,0x90
.globl _fsincos
_fsincos:
    pushl   %ebp
    movl    %esp, %ebp
    fldl    12(%ebp)
    fsincos
    movl    8(%ebp), %eax
    fstpl   8(%eax)
    fstpl   (%eax)
    leave
    ret $4
    .subsections_via_symbols

Tada, il utilise l'instruction fsincos!

37
répondu Chi 2010-04-21 14:46:31

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 elle devrait être très rapide.

13
répondu tanascius 2010-04-21 14:08:52

Techniquement, vous y parviendrez en utilisant des nombres complexes et la formule d'Euler . Ainsi, quelque chose comme (c++)

complex<double> res = exp(complex<double>(0, x));
// or equivalent
complex<double> res = polar<double>(1, x);
double sin_x = res.imag();
double cos_x = res.real();

Devrait vous donner sinus et cosinus en une seule étape. Comment cela est fait en interne est une question de compilateur et de bibliothèque utilisés. Cela pourrait (et pourrait) prendre plus de temps pour le faire de cette façon (juste parce que la formule D'Euler est principalement utilisée pour calculer le complexe exp en utilisant sin et cos - et pas l'inverse) mais il pourrait y avoir un peu de théorie optimisation possible.


Modifier

Les en-têtes de <complex> pour GNU C++ 4.2 utilisent des calculs explicites de sin et cos à l'intérieur de polar, donc cela ne semble pas trop bon pour les optimisations sauf si le compilateur fait de la magie (voir les commutateurs -ffast-math et -mfpmath comme écrit dans la réponse de Chi).

13
répondu Debilski 2017-05-23 12:34:33

Vous pouvez calculer l'un ou l'autre, puis utiliser l'identité:

cos(x)2 = 1 - sin(x)2

Mais comme le dit @tanascius, une table précalculée est la voie à suivre.

12
répondu Mitch Wheat 2010-04-21 15:49:29

Si vous utilisez la bibliothèque GNU C, alors vous pouvez faire:

#define _GNU_SOURCE
#include <math.h>

Et vous obtiendrez des déclarations du sincos(), sincosf() et sincosl() fonctions qui calculent les deux valeurs ensemble-probablement de la manière la plus rapide pour votre architecture cible.

7
répondu caf 2010-04-21 23:54:07

De nombreuses bibliothèques mathématiques C, comme caf l'indique, ont déjà sincos (). L'exception notable est MSVC.

  • Sun a sincos () depuis au moins 1987 (vingt-trois ans; j'ai une page de manuel sur papier)
  • HPUX 11 l'avait en 1997 (mais n'est pas dans HPUX 10.20)
  • ajouté à la glibc dans la version 2.1 (février 1999)
  • est devenu un élément intégré dans gcc 3.4 (2004), __builtin_sincos ().

Et en ce qui concerne 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):

" un autre exemple est de précalculer de petites tables-par exemple, une table de sin (x) par degré pour optimiser les rotations dans un moteur graphique 3D prendre 365 × 4 octets sur une machine moderne. Avant que les processeurs aient assez plus rapide que la mémoire pour exiger la mise en cache, c'était une vitesse évidente optimisation. De nos jours il peut être plus rapide de recalculer à chaque fois plutôt que de payer pour le pourcentage de erreurs de cache causées par le table.

" mais à l'avenir, cela pourrait se retourner à mesure que les caches grossissent. Plus généralement, de nombreuses optimisations sont temporaires et peuvent facilement tourner dans les pessimisations à mesure que les ratios de coûts changent. La seule façon de savoir est de mesurer et voir."(de l'Art de la programmation Unix )

Mais, à en juger par la discussion ci-dessus, tout le monde n'est pas d'accord.

7
répondu Joseph Quinsey 2010-05-03 05:49:43

Il y a des choses très intéressantes sur cette page du forum, qui se concentre sur la recherche de bonnes approximations qui sont rapides: http://www.devmaster.net/forums/showthread.php?t=5784

Avertissement: pas utilisé tout ce genre de choses moi-même.

Mise à jour 22 février 2018: Wayback Machine est la seule façon de visiter la page d'origine maintenant: https://web.archive.org/web/20130927121234/http://devmaster.net/posts/9648/fast-and-accurate-sine-cosine

6
répondu Joel Goodwin 2018-02-22 09:16:45

Je ne crois pas que les tables de recherche soient nécessairement une bonne idée pour ce problème. Sauf si vos exigences de précision sont très faibles, la table doit être très grande. Et les processeurs modernes peuvent faire beaucoup de calcul 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 argument (pas même le mien), tester et mesurer et considérer les données.

Mais je voudrais regarder les implémentations rapides de SinCos que vous trouvez dans les bibliothèques telles que AMD ACML et MKL D'Intel.

5
répondu High Performance Mark 2010-04-21 14:27:27

Si vous êtes prêt à utiliser un produit commercial, et calculez un certain nombre de calculs sin/cos en même temps (de sorte que vous pouvez utiliser des fonctions vectorisées), vous devriez consulter la Bibliothèque du noyau Math D'Intel.

Il est un sincos fonction

Selon cette documentation, il est en moyenne 13.08 horloges / élément sur core 2 duo en mode haute précision, ce qui, je pense, sera encore plus rapide que fsincos.

3
répondu Chi 2010-04-21 15:10:36

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

3
répondu Probes 2010-05-13 09:35:43

Lorsque les performances sont critiques pour ce genre de chose, il n'est pas inhabituel d'introduire une table de recherche.

2
répondu Tom Cabanski 2010-04-21 14:09:40

Pour une approche créative, que diriez-vous d'élargir la série Taylor? Comme ils ont des termes similaires, vous pouvez faire quelque chose comme le pseudo suivant:

numerator = x
denominator = 1
sine = x
cosine = 1
op = -1
fact = 1

while (not enough precision) {
    fact++
    denominator *= fact
    numerator *= x

    cosine += op * numerator / denominator

    fact++
    denominator *= fact
    numerator *= x

    sine += op * numerator / denominator

    op *= -1
}

Cela signifie que vous faites quelque chose comme ceci: en commençant par x et 1 pour le péché et le cosinus, suivez le modèle - soustrayez x^2 / 2! du cosinus, soustrayez x^3 / 3! de sinus, ajouter x^4 / 4! pour cosinus, ajouter x^5 / 5! siné...

Je n'ai aucune idée si ce serait performant. Si vous avez besoin de moins de précision que les sin() et cos intégrés() vous donner, il peut être une option.

2
répondu Tesserex 2010-04-21 14:44:08

Il y a une bonne solution dans la bibliothèque CEPHES qui peut être assez rapide et vous pouvez ajouter / supprimer 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éelles et imaginaires 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 le plus proche de x et écrivons
exp(ix)=exp(iy+(ix-iy))=exp(iy)exp(i(x-y)).

Nous obtenons exp(iy) de la table de recherche. Et puisque |x-y| est petit (au plus la moitié de la distance entre les valeurs y), la série de Taylor convergera bien en quelques termes, donc nous l'utilisons pour exp (i(x-y)). Et puis nous avons juste besoin d'une multiplication complexe pour obtenir exp(ix).

Une autre bonne propriété de ceci est que vous pouvez le vectoriser en utilisant SSE.

2
répondu Jsl 2011-11-04 15:07:04

Vous pouvez jeter un oeil à 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 maximum de sin / cos de l'ordre de 5e-8) et la vitesse (légèrement surclasse fsincos sur une base d'appel unique, et un gagnant clair sur plusieurs valeurs).

2
répondu SleuthEye 2014-05-24 00:40:07

J'ai posté une solution impliquant un assemblage de bras en ligne capable de calculer à la fois le sinus et le cosinus de deux angles à la fois ici: sinus/cosinus rapide pour ARMv7 + NEON

1
répondu jcayzac 2010-05-09 13:14:46

Une approximation précise mais rapide de la fonction sin et cos simultanément, en javascript, peut être trouvée ici: http://danisraelmalta.github.io/Fmath/ (facilement importé en C / C++)

1
répondu user2781980 2013-09-15 20:14:41

Avez-vous pensé à déclarer des tables de recherche pour les deux fonctions? Vous devrez toujours "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.

0
répondu Frank Shearar 2010-04-21 14:11:04