Я хотел бы вычислить как синус, так и косинус значения вместе (например, для создания матрицы вращения). Конечно, я мог бы вычислить их отдельно, одно за другим a = cos(x); b = sin(x);
, но мне интересно, есть ли более быстрый способ, когда нужны оба значения.
Изменить: чтобы обобщить ответы на данный момент:
Влад сказал, что есть команда asm, вычисляющая их
FSINCOS
обоих (почти одновременно с вызовом вFSIN
одиночку)Как заметил Чи , эта оптимизация иногда уже выполняется компилятором (при использовании флагов оптимизации).
caf указал, что функции
sincos
иsincosf
, вероятно, доступны и могут быть вызваны напрямую, просто включивmath.h
Подход tanascius к использованию справочной таблицы является спорным. (Однако на моем компьютере и в тестовом сценарии он работает в 3 раза быстрее, чем
sincos
с почти такой же точностью для 32-битных чисел с плавающей запятой.)Джоэл Гудвин связался с интересным подходом к чрезвычайно быстрой технике аппроксимации с довольно хорошей точностью (для меня это даже быстрее, чем поиск в таблице)
sinx ~ x-x^3/6
иcosx~1-x^2/4
как приближения, если вам важнее скорость, чем точность. Вы можете добавлять термины в любую серию по мере того, как придаете большее значение точности ( en.wikipedia.org/wiki/Taylor_series прокрутите вниз до серии триггеров Тейлора). Обратите внимание, что это общий способ аппроксимации любой функции, которую вы хотите, с дифференцируемымn
временем. Так что, если у вас есть более крупная функция, которой принадлежат эти синусы и косинусы, вы получите гораздо большую скорость, если вы аппроксимируете ее вместо sin, cos независимо.x
близкие к некоторой точкеx_0
, а затем расширьте свою серию Тейлора вокругx_0
вместо 0. Это даст вам отличную точность рядом,x_0
но чем дальше вы тем хуже результаты. Вы, вероятно, думали, что точность - отстой, потому что вы смотрели на данный ответ и пробовали его для значений, далеких от0
. Этот ответ с грехом, cos увеличился примерно до 0.Ответы:
Современные процессоры Intel / AMD имеют инструкции
FSINCOS
по одновременному вычислению синусоидальных и косинусных функций. Если вам нужна сильная оптимизация, возможно, вам стоит ее использовать.Вот небольшой пример: http://home.broadpark.no/~alein/fsincos.html
Вот еще один пример (для MSVC): http://www.codeguru.com/forum/showthread.php?t=328669
Вот еще один пример (с gcc): http://www.allegro.cc/forums/thread/588470
Надеюсь, что один из них поможет. (Сам не использовал эту инструкцию, извините.)
Поскольку они поддерживаются на уровне процессора, я ожидаю, что они будут намного быстрее, чем поиск в таблицах.
Изменить:
Википедия предполагает, что это
FSINCOS
было добавлено на 387 процессорах, поэтому вы вряд ли найдете процессор, который его не поддерживает.Изменить:
в документации Intel указано, что
FSINCOS
это примерно в 5 раз медленнее, чемFDIV
(т.е. деление с плавающей запятой).Изменить:
обратите внимание, что не все современные компиляторы оптимизируют вычисление синуса и косинуса в вызове
FSINCOS
. В частности, мой VS 2008 этого не делал.Изменить:
ссылка на первый пример мертва, но на Wayback Machine все еще есть версия .
источник
fsincos
Инструкция не «достаточно быстро». В собственном руководстве Intel по оптимизации говорится, что на последних микроархитектурах требуется от 119 до 250 циклов. Математическая библиотека от Intel (распределенный с ICC), путем сравнения, можно отдельно вычислитьsin
иcos
менее чем за 100 циклов, используя программную реализацию , которая использует SSE вместо блока x87. Аналогичная программная реализация, которая вычисляла оба одновременно, могла быть еще быстрее.sin
вычислений, которыми можно было бы воспользоваться; они используют те же инструкции SSE, что и все остальные. Что касается вашего второго комментария, скорость относительно неfdiv
имеет значения; если есть два способа сделать что-то и один в два раза быстрее другого, нет смысла называть более медленный «быстрым», независимо от того, сколько времени это занимает по сравнению с какой-то совершенно не связанной задачей.sin
функция в их библиотеке обеспечивает полную точность с двойной точностью.fsincos
Инструкция обеспечивает несколько более высокую точность (двойной продлен), но повышенная точность получает выбрасываются в большинстве программ , которые называютsin
функцию, так как его результат, как правило , округляется до двойной точности попозже арифметических операций или магазин в памяти. В большинстве случаев они обеспечивают одинаковую точность для практического использования.fsincos
это не полная реализация сама по себе; вам потребуется дополнительный шаг уменьшения диапазона, чтобы поместить аргумент в допустимый диапазон ввода дляfsincos
инструкции. Библиотекаsin
иcos
функции включают это сокращение, а также основные вычисления, поэтому они даже быстрее (по сравнению), чем может указывать время цикла, которое я указал.В современных процессорах x86 есть инструкция fsincos, которая будет делать именно то, что вы просите - вычислять sin и cos одновременно. Хороший оптимизирующий компилятор должен обнаруживать код, который вычисляет sin и cos для одного и того же значения, и использовать команду fsincos для его выполнения.
Чтобы это сработало, потребовалось немного перевернуть флаги компилятора, но:
Тада, пользуется инструкцией fsincos!
источник
-ffast-math
и-mfpmath
в некоторых случаях приводят к разным результатам.fsin
иfcos
. :-(__CIsin
и__CIcos
.Когда вам нужна производительность, вы можете использовать предварительно вычисленную таблицу sin / cos (подойдет одна таблица, сохраненная в виде словаря). Ну, это зависит от необходимой точности (возможно, таблица будет слишком большой), но это должно быть очень быстро.
источник
sin
потому что предварительно вычисленная таблица будет уничтожать кеш.Технически этого можно добиться, используя комплексные числа и формулу Эйлера . Таким образом, что-то вроде (C ++)
должен дать вам синус и косинус за один шаг. Как это делается внутри, зависит от используемого компилятора и библиотеки. Это может (и может) занять больше времени, чтобы сделать это таким образом (просто потому, что формула Эйлера в основном используется для вычисления сложных
exp
с использованиемsin
иcos
- а не наоборот), но возможна некоторая теоретическая оптимизация.редактировать
Заголовки в
<complex>
ГНУ C ++ 4.2 используют явные вычисленияsin
иcos
внутриpolar
, поэтому он не выглядит слишком хорошо для оптимизаций там , если компилятор не делает некоторые магии (см-ffast-math
и-mfpmath
переключатели , как написано в ответ Чи ).источник
Вы можете вычислить любое из них, а затем использовать идентификатор:
но, как говорит @tanascius, заранее вычисленная таблица - это путь.
источник
sqrt()
часто оптимизируется аппаратно, поэтому вполне может быть быстрее, чемsin()
илиcos()
. Сила - это просто умножение себя, так что не используйтеpow()
. Есть несколько приемов, позволяющих очень быстро получить достаточно точный квадратный корень без аппаратной поддержки. Наконец, не забудьте профилировать, прежде чем делать что-либо из этого.Если вы используете библиотеку GNU C, вы можете:
и вы получите объявления функций
sincos()
,sincosf()
и,sincosl()
которые вычисляют оба значения вместе - вероятно, самым быстрым способом для вашей целевой архитектуры.источник
На этой странице форума есть очень интересные материалы, которые ориентированы на быстрый поиск хороших приближений: http://www.devmaster.net/forums/showthread.php?t=5784
Отказ от ответственности: Я не использовал ничего из этого.
Обновление от 22 февраля 2018 г .: Wayback Machine - единственный способ посетить исходную страницу сейчас: https://web.archive.org/web/20130927121234/http://devmaster.net/posts/9648/fast-and-accurate- синус-косинус
источник
Многие математические библиотеки C, как указывает caf, уже имеют sincos (). Заметным исключением является MSVC.
Что касается поиска, Эрик С. Реймонд в книге « Искусство программирования Unix» (2004) (глава 12) прямо говорит, что это плохая идея (в настоящий момент):
Но, судя по изложенному выше, не все согласны.
источник
fsincos
попробовал бы (инструкция ЦП!) Для остальных. Часто это так же быстро, как интерполяция sin и cos из большой таблицы.Я не верю, что справочные таблицы обязательно являются хорошей идеей для решения этой проблемы. Если ваши требования к точности не очень низкие, таблица должна быть очень большой. А современные процессоры могут выполнять много вычислений, пока значение извлекается из основной памяти. Это не один из тех вопросов, на которые можно правильно ответить с помощью аргументов (даже не моих), тестирования, измерения и рассмотрения данных.
Но я бы посмотрел на быстрые реализации SinCos, которые вы найдете в таких библиотеках, как AMD ACML и Intel MKL.
источник
Если вы хотите использовать коммерческий продукт и одновременно рассчитываете несколько вычислений sin / cos (чтобы вы могли использовать векторные функции), вам следует обратиться к библиотеке Intel Math Kernel Library.
Имеет функцию синкос
Согласно этой документации, он в среднем составляет 13,08 тактов на элемент на дуэте Core 2 в режиме высокой точности, что, я думаю, будет даже быстрее, чем fsincos.
источник
vvsincos
илиvvsincosf
из него. Я считаю, что у AMD есть аналогичные функции в своей векторной библиотеке.В этой статье показано, как построить параболический алгоритм, который генерирует как синус, так и косинус:
Уловка DSP: одновременное параболическое приближение Sin и Cos
http://www.dspguru.com/dsp/tricks/parabolic-approximation-of-sin-and-cos
источник
Когда производительность критична для такого рода вещей, нередко вводится таблица поиска.
источник
Что касается творческого подхода, как насчет расширения серии Тейлора? Поскольку у них похожие термины, вы можете сделать что-то вроде следующего псевдонима:
Это означает, что вы делаете что-то вроде этого: начиная с x и 1 для sin и косинуса, следуйте шаблону - вычтите x ^ 2/2! из косинуса вычтите x ^ 3/3! из синуса добавить x ^ 4/4! к косинусу прибавить x ^ 5/5! синус ...
Я понятия не имею, будет ли это работать. Если вам нужна меньшая точность, чем дают встроенные функции sin () и cos (), это может быть вариантом.
источник
В библиотеке CEPHES есть хорошее решение, которое может быть довольно быстрым, и вы можете довольно гибко добавлять / удалять точность, немного больше / меньше процессорного времени.
Помните, что cos (x) и sin (x) - это действительная и мнимая части exp (ix). Итак, мы хотим вычислить exp (ix), чтобы получить и то, и другое. Мы предварительно вычисляем exp (iy) для некоторых дискретных значений y между 0 и 2pi. Сдвигаем x на интервал [0, 2pi). Затем мы выбираем y, ближайший к x, и пишем
exp (ix) = exp (iy + (ix-iy)) = exp (iy) exp (i (xy)).
Мы получаем exp (iy) из справочной таблицы. А поскольку | xy | мала (не более половины расстояния между значениями y), ряд Тейлора будет хорошо сходиться всего за несколько членов, поэтому мы используем это для exp (i (xy)). И тогда нам просто нужно комплексное умножение, чтобы получить exp (ix).
Еще одним приятным свойством этого является то, что вы можете векторизовать его с помощью SSE.
источник
Возможно, вы захотите взглянуть на http://gruntthepeon.free.fr/ssemath/ , который предлагает векторизованную реализацию SSE, вдохновленную библиотекой CEPHES. Он имеет хорошую точность (максимальное отклонение от sin / cos порядка 5e-8) и скорость (немного превосходит fsincos на основе одного вызова и явный победитель над несколькими значениями).
источник
Я опубликовал решение, включающее встроенную сборку ARM, способную вычислять как синус, так и косинус двух углов одновременно здесь: Быстрый синус / косинус для ARMv7 + NEON
источник
Точное, но быстрое приближение функций sin и cos одновременно в javascript можно найти здесь: http://danisraelmalta.github.io/Fmath/ (легко импортируется в c / c ++)
источник
Думали ли вы об объявлении таблиц поиска для двух функций? Вам все равно придется «вычислять» sin (x) и cos (x), но это будет значительно быстрее, если вам не нужна высокая степень точности.
источник
Компилятор MSVC может использовать (внутренние) функции SSE2
в оптимизированных сборках, если указаны соответствующие флаги компилятора (как минимум / O2 / arch: SSE2 / fp: fast). Имена этих функций, по-видимому, подразумевают, что они вычисляют не отдельные sin и cos, а вычисляют обе «за один шаг».
Например:
Сборка (для x86) с / fp: fast:
Сборка (для x86) без / fp: fast, но с / fp: precision вместо этого (что по умолчанию) вызывает отдельные sin и cos:
Итак, / fp: fast является обязательным для оптимизации sincos.
Но учтите, что
может быть не так точно, как
из-за отсутствия «точного» в конце названия.
На моей «немного» более старой системе (Intel Core 2 Duo E6750) с последним компилятором MSVC 2019 и соответствующими оптимизациями мой тест показывает, что вызов sincos примерно в 2,4 раза быстрее, чем отдельные вызовы sin и cos.
источник