Какой самый быстрый способ вычислить грех и cos вместе? - PullRequest
98 голосов
/ 21 апреля 2010

Я хотел бы вычислить синус и косинус значения вместе (например, чтобы создать матрицу вращения). Конечно, я мог бы вычислять их отдельно один за другим, как a = cos(x); b = sin(x);, но мне интересно, есть ли более быстрый способ, когда нужны оба значения.

Edit: Подведем итоги ответов на данный момент:

  • Влад сказал, что есть команда asm FSINCOS, вычисляющая их обоих (почти одновременно с вызовом только FSIN)

  • Как и Chi заметил, что эта оптимизация иногда уже выполняется компилятором (при использовании флагов оптимизации).

  • caf отметил, что функции sincos и sincosf, вероятно, доступны и могут вызываться напрямую, просто включив math.h

  • tanascius Подход с использованием справочной таблицы обсуждается спорный. (Однако на моем компьютере и в тестовом сценарии он работает в 3 раза быстрее, чем sincos с почти такой же точностью для 32-разрядных чисел с плавающей запятой.)

  • Джоэл Гудвин связан с интересным подходом техники чрезвычайно быстрого приближения с довольно хорошей точностью (для меня это даже быстрее, чем поиск по таблице)

Ответы [ 19 ]

3 голосов
/ 21 апреля 2010

Если вы хотите использовать коммерческий продукт и рассчитываете несколько расчетов sin / cos одновременно (чтобы вы могли использовать векторные функции), вам следует проверить Math Kernel Library от Intel.

Имеет функцию sincos

В соответствии с этой документацией, он в среднем составляет 13,08 такта / элемент на Core 2 Duo в режиме высокой точности, что, я думаю, будет даже быстрее, чем fsincos.

2 голосов
/ 24 мая 2014

Возможно, вы захотите взглянуть на http://gruntthepeon.free.fr/ssemath/,, который предлагает векторизованную реализацию SSE, основанную на библиотеке CEPHES. Он имеет хорошую точность (максимальное отклонение от sin / cos порядка 5e-8) и скорость (немного превосходит fsincos на основе одного вызова и явный победитель по нескольким значениям).

2 голосов
/ 04 ноября 2011

В библиотеке CEPHES есть отличное решение, которое может быть довольно быстрым, и вы можете довольно гибко добавлять / удалять точность, увеличивая / уменьшая время процессора.

Помните, что cos (x) и sin (x) - это действительная и мнимая части exp (ix).Поэтому мы хотим вычислить exp (ix), чтобы получить оба.Мы предварительно вычислим exp (iy) для некоторых дискретных значений y между 0 и 2pi.Сдвинем x на интервал [0, 2pi).Затем мы выбираем y, ближайший к x, и записываемехр (IX) = ехр (гу + (IX-гу)) = ехр (гу) ехр (я (х)).

Мы получаем exp (iy) из таблицы поиска.И так как | ху |мал (не более половины расстояния между значениями y), ряд Тейлора будет хорошо сходиться всего за несколько терминов, поэтому мы используем это для exp (i (xy)).И тогда нам просто нужно сложное умножение, чтобы получить exp (ix).

Еще одно приятное свойство этого заключается в том, что вы можете векторизовать его, используя SSE.

2 голосов
/ 21 апреля 2010

Когда производительность важна для такого рода вещей, нет ничего необычного в том, чтобы ввести таблицу поиска.

2 голосов
/ 21 апреля 2010

Для творческого подхода, как насчет расширения серии Тейлор?Поскольку они имеют похожие термины, вы можете сделать что-то вроде следующего псевдо:

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
}

Это означает, что вы делаете что-то вроде этого: начиная с x и 1 для греха и косинуса, следуйте шаблону - вычтите x ^ 2/ 2!от косинуса, вычтите х ^ 3/3!от синуса, добавьте х ^ 4/4!к косинусу, добавьте х ^ 5/5!к синусу ...

Я понятия не имею, будет ли это производительным.Если вам нужна меньшая точность, чем позволяют встроенные функции sin () и cos (), это может быть вариантом.

1 голос
/ 16 сентября 2013

Точное, но быстрое приближение функций sin и cos одновременно в javascript можно найти здесь: http://danisraelmalta.github.io/Fmath/ (легко импортируется в c / c ++)

1 голос
/ 09 мая 2010

Я опубликовал решение, включающее встроенную сборку ARM, способную одновременно вычислять синус и косинус двух углов: Быстрый синус / косинус для ARMv7 + NEON

0 голосов
/ 30 июня 2019

Компилятор MSVC может использовать (внутренние) функции

 ___libm_sse2_sincos_ (for x86)
 __libm_sse2_sincos_  (for x64)

в оптимизированных сборках, если указаны соответствующие флаги компилятора (как минимум / O2 / arch: SSE2 / fp: fast). Названия этих функций, по-видимому, подразумевают, что они не вычисляют отдельные sin и cos, а оба «за один шаг».

Например:

void sincos(double const x, double & s, double & c)
{
  s = std::sin(x);
  c = std::cos(x);
}

Сборка (для x86) с / fp: fast:

movsd   xmm0, QWORD PTR _x$[esp-4]
call    ___libm_sse2_sincos_
mov     eax, DWORD PTR _s$[esp-4]
movsd   QWORD PTR [eax], xmm0
mov     eax, DWORD PTR _c$[esp-4]
shufpd  xmm0, xmm0, 1
movsd   QWORD PTR [eax], xmm0
ret     0

Сборка (для x86) без / fp: быстрая, но с / fp: точная (вместо этого (по умолчанию)) вызывает отдельный sin и cos:

movsd   xmm0, QWORD PTR _x$[esp-4]
call    __libm_sse2_sin_precise
mov     eax, DWORD PTR _s$[esp-4]
movsd   QWORD PTR [eax], xmm0
movsd   xmm0, QWORD PTR _x$[esp-4]
call    __libm_sse2_cos_precise
mov     eax, DWORD PTR _c$[esp-4]
movsd   QWORD PTR [eax], xmm0
ret     0

Итак, / fp: fast обязателен для оптимизации sincos.

Но учтите, что

___libm_sse2_sincos_

возможно не так точно, как

__libm_sse2_sin_precise
__libm_sse2_cos_precise

из-за пропущенного «точного» в конце названия.

0 голосов
/ 21 апреля 2010

Задумывались ли вы об объявлении справочных таблиц для двух функций? Вам все равно придется «вычислять» sin (x) и cos (x), но это будет гораздо быстрее, если вам не нужна высокая степень точности.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...