Me gustaría calcular el seno y el coseno de un valor juntos (por ejemplo, para crear una matriz de rotación). Por supuesto, podría calcularlos por separado uno tras otro a = cos(x); b = sin(x);
, pero me pregunto si hay una forma más rápida cuando se necesitan ambos valores.
Editar: para resumir las respuestas hasta ahora:
Vlad dijo que existe el comando asm que los
FSINCOS
calcula a ambos (casi al mismo tiempo que una llamada aFSIN
solo)Como advirtió Chi , esta optimización a veces ya la realiza el compilador (cuando se utilizan indicadores de optimización).
caf señaló, que las funciones
sincos
ysincosf
probablemente están disponibles y se pueden llamar directamente con solo incluirmath.h
El enfoque de tanascius del uso de una tabla de consulta es controvertido. (Sin embargo, en mi computadora y en un escenario de referencia, se ejecuta 3 veces más rápido que
sincos
con casi la misma precisión para puntos flotantes de 32 bits).Joel Goodwin se vinculó a un enfoque interesante de una técnica de aproximación extremadamente rápida con una precisión bastante buena (para mí, esto es incluso más rápido que la búsqueda de tablas)
sinx ~ x-x^3/6
ycosx~1-x^2/4
como aproximaciones si le importa la velocidad más que la precisión. Puede agregar términos en cualquiera de las series a medida que le da más peso a la precisión ( en.wikipedia.org/wiki/Taylor_series, desplácese hacia abajo hasta la serie trig taylor). Tenga en cuenta que esta es una forma general de aproximar cualquier función que desee que sea den
tiempos diferenciables . Entonces, si tiene una función más grande a la que pertenecen el seno y el coseno, obtendrá una velocidad mucho mayor si la aproxima en lugar del pecado, el cos de forma independiente.x
cercanos a algún puntox_0
, luego expanda su serie de Taylor enx_0
lugar de 0. Esto le dará una excelente precisión cerca,x_0
pero cuanto más lejos esté vaya peor los resultados. Probablemente pensó que la precisión es una porquería cuando miró la respuesta dada y la probó con valores alejados de0
. Esa respuesta es sin, cos expandido alrededor de 0.Respuestas:
Los procesadores Intel / AMD modernos tienen instrucciones
FSINCOS
para calcular funciones de seno y coseno simultáneamente. Si necesita una optimización sólida, tal vez debería usarla.Aquí hay un pequeño ejemplo: http://home.broadpark.no/~alein/fsincos.html
Aquí hay otro ejemplo (para MSVC): http://www.codeguru.com/forum/showthread.php?t=328669
Aquí hay otro ejemplo (con gcc): http://www.allegro.cc/forums/thread/588470
Espero que alguno de ellos ayude. (No utilicé esta instrucción, lo siento).
Como son compatibles a nivel de procesador, espero que sean mucho más rápidos que las búsquedas de tablas.
Editar:
Wikipedia sugiere que
FSINCOS
se agregó en 387 procesadores, por lo que difícilmente puede encontrar un procesador que no lo admita.Editar:
la documentación de Intel indica que
FSINCOS
es aproximadamente 5 veces más lento queFDIV
(es decir, división de punto flotante).Editar:
tenga en cuenta que no todos los compiladores modernos optimizan el cálculo de seno y coseno en una llamada a
FSINCOS
. En particular, mi VS 2008 no lo hizo de esa manera.Editar:
El primer enlace de ejemplo está muerto, pero todavía hay una versión en Wayback Machine .
fuente
fsincos
instrucción no es "bastante rápida". El propio manual de optimización de Intel lo cita como que requiere entre 119 y 250 ciclos en microarquitecturas recientes. La biblioteca matemática de Intel (distribuida con ICC), en comparación, puede calcular por separadosin
ycos
en menos de 100 ciclos, utilizando una implementación de software que usa SSE en lugar de la unidad x87. Una implementación de software similar que calculó ambos simultáneamente podría ser aún más rápida.sin
Sin embargo, puedo decirles que no hay ningún cálculo integrado que puedan aprovechar; utilizan las mismas instrucciones SSE que todos los demás. Para su segundo comentario, la velocidad relativa afdiv
es irrelevante; si hay dos formas de hacer algo y una es dos veces más rápida que la otra, no tiene sentido llamar "rápida" a la más lenta, independientemente del tiempo que tome en relación con una tarea completamente no relacionada.sin
función de software de su biblioteca ofrece una precisión total de doble precisión. Lafsincos
instrucción ofrece algo más de precisión (doble extendida), pero esa precisión adicional se descarta en la mayoría de los programas que llaman a lasin
función, ya que su resultado generalmente se redondea a doble precisión mediante operaciones aritméticas posteriores o un almacenamiento en la memoria. En la mayoría de las situaciones, ofrecen la misma precisión para un uso práctico.fsincos
no es una implementación completa por sí sola; necesita un paso de reducción de rango adicional para poner el argumento en el rango de entrada válido para lafsincos
instrucción. La bibliotecasin
y lascos
funciones incluyen esta reducción, así como el cálculo del núcleo, por lo que son incluso más rápidos (en comparación) de lo que podrían indicar los tiempos de ciclo que enumeré.Los procesadores x86 modernos tienen una instrucción fsincos que hará exactamente lo que está pidiendo: calcular sin y cos al mismo tiempo. Un buen compilador de optimización debería detectar el código que calcula sin y cos para el mismo valor y usar el comando fsincos para ejecutarlo.
Se necesitaron algunos juegos de banderas del compilador para que esto funcionara, pero:
¡Tada, usa la instrucción fsincos!
fuente
-ffast-math
y-mfpmath
lleve a resultados diferentes en algunos casos.fsin
yfcos
. :-(__CIsin
y__CIcos
.Cuando necesite rendimiento, puede usar una tabla sin / cos precalculada (una tabla servirá, almacenada como un diccionario). Bueno, depende de la precisión que necesites (quizás la mesa sea demasiado grande), pero debería ser muy rápido.
fuente
sin
porque la tabla precalculada destruirá la caché.Técnicamente, lograrías esto usando números complejos y la fórmula de Euler . Por lo tanto, algo como (C ++)
debería darte seno y coseno en un solo paso. Cómo se hace esto internamente depende del compilador y la biblioteca que se utilicen. Podría (y podría) llevar más tiempo hacerlo de esta manera (solo porque la Fórmula de Euler se usa principalmente para calcular el complejo
exp
usandosin
ycos
, y no al revés) pero podría haber alguna optimización teórica posible.Editar
Los encabezados
<complex>
de GNU C ++ 4.2 utilizan cálculos explícitos desin
ycos
adentropolar
, por lo que no se ve demasiado bien para las optimizaciones allí a menos que el compilador haga algo de magia (vea los interruptores-ffast-math
y-mfpmath
como está escrito en la respuesta de Chi ).fuente
Puede calcular cualquiera y luego usar la identidad:
pero como dice @tanascius, una tabla precalculada es el camino a seguir.
fuente
sqrt()
a menudo está optimizado en hardware, por lo que puede ser más rápido entoncessin()
ocos()
. El poder es solo una auto multiplicación, así que no lo usespow()
. Existen algunos trucos para obtener raíces cuadradas razonablemente precisas muy rápidamente sin soporte de hardware. Por último, asegúrese de crear un perfil antes de hacer nada de esto.Si usa la biblioteca GNU C, entonces puede hacer:
y obtendrá declaraciones de las funciones
sincos()
,sincosf()
ysincosl()
que calculan ambos valores juntos, presumiblemente de la manera más rápida para su arquitectura de destino.fuente
Hay cosas muy interesantes en esta página del foro, que se centra en encontrar buenas aproximaciones que sean rápidas: http://www.devmaster.net/forums/showthread.php?t=5784
Descargo de responsabilidad: no utilicé nada de esto yo mismo.
Actualización 22 de febrero de 2018: Wayback Machine es la única forma de visitar la página original ahora: https://web.archive.org/web/20130927121234/http://devmaster.net/posts/9648/fast-and-accurate- seno-coseno
fuente
Muchas bibliotecas matemáticas de C, como indica caf, ya tienen sincos (). La excepción notable es MSVC.
Y con respecto a la búsqueda, Eric S. Raymond en Art of Unix Programming (2004) (Capítulo 12) dice explícitamente que esta es una mala idea (en el momento actual):
Pero, a juzgar por la discusión anterior, no todos están de acuerdo.
fuente
fsincos
probaría (¡instrucción de CPU!) Para los demás. A menudo es tan rápido como interpolar pecado y cos de una tabla grande.No creo que las tablas de búsqueda sean necesariamente una buena idea para este problema. A menos que sus requisitos de precisión sean muy bajos, la mesa debe ser muy grande. Y las CPU modernas pueden realizar muchos cálculos mientras se obtiene un valor de la memoria principal. Esta no es una de esas preguntas que pueden responderse adecuadamente con un argumento (ni siquiera el mío), probar y medir y considerar los datos.
Pero buscaría las implementaciones rápidas de SinCos que se encuentran en bibliotecas como ACML de AMD y MKL de Intel.
fuente
Si está dispuesto a utilizar un producto comercial y está calculando una serie de cálculos sin / cos al mismo tiempo (para poder utilizar funciones vectoriales), debería consultar la biblioteca de kernel matemática de Intel.
Tiene una función sincos
De acuerdo con esa documentación, promedia 13.08 relojes / elemento en core 2 duo en modo de alta precisión, que creo que será incluso más rápido que fsincos.
fuente
vvsincos
ovvsincosf
desde Accelerate.framework. Creo que AMD también tiene funciones similares en su biblioteca de vectores.Este artículo muestra cómo construir un algoritmo parabólico que genera tanto el seno como el coseno:
Truco DSP: Aproximación parabólica simultánea de Sin y Cos
http://www.dspguru.com/dsp/tricks/parabolic-approximation-of-sin-and-cos
fuente
Cuando el rendimiento es fundamental para este tipo de cosas, no es inusual introducir una tabla de búsqueda.
fuente
Para un enfoque creativo, ¿qué tal expandir la serie Taylor? Dado que tienen términos similares, podría hacer algo como el siguiente pseudo:
Esto significa que haces algo como esto: comenzando en xy 1 para el pecado y el coseno, sigue el patrón: reste x ^ 2/2. del coseno, reste x ^ 3/3! desde el seno, agregue x ^ 4/4! al coseno, suma x ^ 5/5! a seno ...
No tengo idea de si esto funcionaría bien. Si necesita menos precisión que la incorporada sin () y cos (), puede ser una opción.
fuente
Hay una buena solución en la biblioteca CEPHES que puede ser bastante rápida y puede agregar / eliminar precisión de manera bastante flexible por un poco más / menos de tiempo de CPU.
Recuerde que cos (x) y sin (x) son las partes real e imaginaria de exp (ix). Entonces queremos calcular exp (ix) para obtener ambos. Calculamos previamente exp (iy) para algunos valores discretos de y entre 0 y 2pi. Cambiamos x al intervalo [0, 2pi). Luego seleccionamos la y que está más cerca de x y escribimos
exp (ix) = exp (iy + (ix-iy)) = exp (iy) exp (i (xy)).
Obtenemos exp (iy) de la tabla de búsqueda. Y desde | xy | es pequeña (como mucho la mitad de la distancia entre los valores de y), la serie de Taylor convergerá muy bien en unos pocos términos, por lo que la usamos para exp (i (xy)). Y luego solo necesitamos una multiplicación compleja para obtener exp (ix).
Otra buena propiedad de esto es que puedes vectorizarlo usando SSE.
fuente
Es posible que desee echar un vistazo a http://gruntthepeon.free.fr/ssemath/ , que ofrece una implementación vectorizada SSE inspirada en la biblioteca CEPHES. Tiene buena precisión (desviación máxima de sin / cos en el orden de 5e-8) y velocidad (supera ligeramente a fsincos en una sola llamada y un claro ganador sobre múltiples valores).
fuente
He publicado una solución que implica un ensamblaje ARM en línea capaz de calcular tanto el seno como el coseno de dos ángulos a la vez aquí: seno / coseno rápido para ARMv7 + NEON
fuente
Se puede encontrar una aproximación precisa pero rápida de la función sin y cos simultáneamente, en javascript, aquí: http://danisraelmalta.github.io/Fmath/ (se importa fácilmente a c / c ++)
fuente
¿Ha pensado en declarar tablas de búsqueda para las dos funciones? Aún tendría que "calcular" sin (x) y cos (x), pero sería decididamente más rápido, si no necesita un alto grado de precisión.
fuente
El compilador de MSVC puede utilizar las funciones SSE2 (internas)
en compilaciones optimizadas si se especifican los indicadores del compilador apropiados (al mínimo / O2 / arch: SSE2 / fp: fast). Los nombres de estas funciones parecen implicar que no calculan sen y cos por separado, sino que ambos "en un solo paso".
Por ejemplo:
Ensamblado (para x86) con / fp: rápido:
Ensamblaje (para x86) sin / fp: rápido pero con / fp: preciso en su lugar (que es el predeterminado) llama sin y cos por separado:
Entonces / fp: fast es obligatorio para la optimización de sincos.
Pero tenga en cuenta que
tal vez no sea tan preciso como
debido a la falta "precisa" al final de su nombre.
En mi sistema "ligeramente" más antiguo (Intel Core 2 Duo E6750) con el último compilador MSVC 2019 y las optimizaciones apropiadas, mi punto de referencia muestra que la llamada sincos es aproximadamente 2,4 veces más rápida que las llamadas sin y cos por separado.
fuente