110 votes

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

Je voudrais calculer à la fois le sinus et le cosinus 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 suit a = cos(x); b = sin(x); mais je me demande s'il n'y a pas une méthode plus rapide pour avoir besoin des deux valeurs.

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

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

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

  • caf a souligné, que les fonctions sincos y sincosf sont probablement disponibles et peuvent être appelés directement en incluant simplement math.h

  • tanascius L'approche consistant à utiliser une table de consultation est controversée (toutefois, sur mon ordinateur et dans le cadre d'un scénario de référence, il fonctionne trois fois plus vite que la table de consultation). sincos avec presque la même précision pour les points flottants 32 bits).

  • Joel Goodwin J'ai trouvé un lien vers 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 consultation de tables).

1 votes

Voir aussi cette question sur l'implémentation native de sin/cos : stackoverflow.com/questions/1640595

1 votes

Essayez sinx ~ x-x^3/6 y cosx~1-x^2/4 comme des 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 si vous accordez plus d'importance à la précision ( fr.wikipedia.org/wiki/Taylor_series défilement vers le bas pour la série trig taylor.) Notez qu'il s'agit d'un moyen général d'approximer n'importe quelle fonction differnable. n temps. Ainsi, si vous avez une fonction plus importante à laquelle appartiennent les sinus et les cosinus, vous obtiendrez un gain de vitesse bien plus important si vous l'approchez au lieu d'utiliser les sin et les cos indépendamment.

0 votes

Il s'agit d'une mauvaise technique avec une très mauvaise précision. Voir le post de Joel Goodwin. Les séries de Taylor ont été postées ci-dessous. Veuillez le poster comme réponse.

55voto

Vlad Points 23480

Moderne Intel/AMD, les processeurs d'instruction FSINCOS pour le calcul de sinus et cosinus fonctions simultanément. Si vous avez besoin d'une forte optimisation, peut-être que vous devriez 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

Espérons que l'un d'eux. (Je n'ai pas utiliser cette instruction moi-même, désolé.)

Comme ils sont pris en charge sur le processeur, je m'attends à être de façon beaucoup plus rapide que le tableau des recherches.

Edit:
Wikipédia suggère qu' FSINCOS a été ajouté à 387 processeurs, de sorte que vous pouvez difficilement trouver un processeur qui ne le supporte pas.

Edit:
Intel documentation états qu' FSINCOS est à peu près 5 fois plus lent que l' FDIV (c'est à dire, floating point de la division).

Edit:
Veuillez noter que tous les compilateurs modernes d'optimiser le calcul de sinus et cosinus dans un appel à l' FSINCOS. En particulier, mon VS 2008 n'a pas le faire de cette façon.

Edit:
Le premier exemple, le lien est mort, mais il y a toujours une version à la Wayback Machine.

0 votes

Avez-vous une référence à ce sujet ou un extrait de la manière de l'appeler en C ou C# ?

0 votes

Les compilateurs sont-ils assez intelligents pour utiliser cette instruction au cas où vous écririez : x=cos(a);y=sin(a) ; Ce serait génial.

1 votes

@phkahler : Ce serait génial. Je ne sais pas si une telle optimisation est utilisée par les compilateurs modernes.

42voto

Chi Points 8991

Les processeurs x86 modernes ont une instruction fsincos qui fait exactement ce que vous demandez - calculer le sin et le cos en même temps. Un bon compilateur optimisateur devrait détecter le code qui calcule sin et cos pour la même valeur et utiliser l'instruction fsincos pour l'exécuter.

Il a fallu modifier les paramètres 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 !

0 votes

C'est cool ! Pourriez-vous expliquer ce que fait -mfpmath=387 ? Et cela fonctionne-t-il aussi avec MSVC ?

1 votes

Notez que -ffast-math y -mfpmath conduisent à des résultats différents dans certains cas.

3 votes

Mfpmath=387 forcera gcc à utiliser les instructions x87 au lieu des instructions SSE. Je pense que MSVC a des optimisations et des drapeaux similaires, mais je n'ai pas MSVC sous la main pour en être sûr. L'utilisation des instructions x87 sera probablement un inconvénient pour les performances dans d'autres codes, vous devriez également regarder mon autre réponse, pour utiliser la MKL d'Intel.

14voto

Debilski Points 28586

Techniquement, vous y arriveriez 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 le sinus et le cosinus en une seule étape. La façon dont cela est fait en interne est une question de compilateur et de bibliothèque utilisés. Cela pourrait (et pourrait) bien prendre plus de temps de le faire de cette façon (simplement parce que la formule d'Euler est surtout utilisée pour calculer le complexe exp en utilisant sin y cos - et non l'inverse) mais une optimisation théorique est peut-être possible.


Modifier

Les en-têtes dans <complex> pour GNU C++ 4.2 utilisent des calculs explicites de sin y cos à l'intérieur de polar Il est donc peu probable que des optimisations soient possibles à cet endroit, à moins que le compilateur ne fasse quelque chose de magique (voir l'onglet -ffast-math y -mfpmath les commutateurs comme indiqué dans La réponse de Chi ).

1 votes

Désolé, mais la formule d'Euler ne vous dit pas vraiment comment pour calculer quelque chose, il s'agit simplement d'une identité (bien que très utile) qui relie les exponentielles complexes aux fonctions trigonométriques réelles. Il y a des avantages à calculer le sinus et le cosinus ensemble, mais ils impliquent des sous-expressions communes et votre réponse n'en parle pas.

14voto

tanascius Points 22712

Lorsque vous avez besoin de performances, vous pouvez utiliser une table de sin/cos précalculée (une seule table suffit, stockée sous forme de dictionnaire). Cela dépend de la précision dont vous avez besoin (la table serait peut-être trop grande), mais cela devrait être très rapide.

0 votes

Ensuite, la valeur d'entrée doit être mappée à [0,2*pi] (ou plus petit avec des vérifications supplémentaires) et cet appel à fmod mange la performance. Dans mon implémentation (probablement sous-optimale), je n'ai pas pu gagner en performance avec la table de recherche. Auriez-vous un conseil à me donner ?

11 votes

Une table précalculée sera presque certainement plus lente que le simple fait d'appeler sin parce que la table précalculée va détruire le cache.

1 votes

Cela dépend de la taille de la table. Une table de 256 entrées est souvent assez précise et n'utilise que 1 Ko... si vous l'utilisez beaucoup, ne risque-t-elle pas de rester bloquée dans le cache sans nuire aux performances du reste de l'application ?

12voto

Mitch Wheat Points 169614

Vous pourriez calculer l'un ou l'autre et ensuite utiliser l'identité :

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

mais comme le dit @tanascius, une table précalculée est la meilleure solution.

8 votes

Et sachez que l'utilisation de cette méthode implique le calcul d'une puissance et d'une racine carrée. Si les performances sont importantes, vérifiez que cette méthode est plus rapide que le calcul direct de l'autre fonction trigonométrique.

5 votes

sqrt() est souvent optimisé dans le matériel, il peut donc très bien être plus rapide que sin() o cos() . La puissance est juste une auto-multiplication, donc n'utilisez pas pow() . Il existe quelques astuces pour obtenir des racines carrées raisonnablement précises très rapidement sans support matériel. Enfin, n'oubliez pas d'établir un profil avant de faire tout cela.

0 votes

Il s'agit d'une approche intéressante, bien que les racines carrées supplémentaires et les vérifications du bon signe puissent réduire les performances. Des conseils concrets ?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X