Ускорение тригонометрических и прочих функций в bc
Интересно, что вся стандартная библиотека (подключаемая через ключ -l) для bc написана нативно. С одной стороны, это не очень-то здорово, так как не использует всю мощь современных процессоров для вычисления различных функций, с другой стороны, эти функции можно свободно переопределить, если мы готовы пожертвовать точностью.
Я тут потихоньку ускоряю одну из своих программ на bc, но все «низко висящие фрукты» я уже подобрал. Добрался до тригонометрических функций. Во внутренней библиотеке косинус выражается через синус, так что, если ускорить синус, то ускорится сразу и косинус.
Посмотрел разные реализации, в итоге портировал функцию, которую взял из одной из статей на «Хабре», получилось так (используется многочлен Чебышёва):
define mod(a, b) {
auto s, m
s = scale; scale = 0; m = a$ % b$; scale = s; return m
}
define s(x) {
auto sign, xf, per, xx, y
x *= 0.63661977236758134308
if (sign = (x < 0)) x = -x
xf = x$
x -= xf
if (mod(xf, 2)) x = 1 - x
per = mod(xf / 2, 2)
xx = x * x
y = x * (1.5707903005870776 + xx * (-0.6458858977085938 + \
xx*(0.07941798513358536 - 0.0043223880120647346 * xx)))
if (sign != per) return -y
return y
}
Расхождение со встроенной в bc реализацией начинается в седьмом знаке, это более чем достаточно для меня, зато вычисление синуса ускорилось больше, чем в четыре раза.
Добавлено позднее: ещё я заменил функцию нахождения логарифма по основанию десять. Логарифмом я находил количество разрядов числа, чтобы потом вывести его на экран, так что его легко было заменить на цикл с подсчётом порядка делением. Разница в производительности — больше, чем в 60 раз. Код теперь выглядит так:
define cnt_digits(x) {
auto i, s
s = scale; scale = 0
for (i = 0; x /= 10; i++){}
scale = s; return i
}
Спасибо, поправил! Писал уже перед сном.
Помню, как в старых флеш-играх нужно было таблицу синусов запекать на старте, иногда считать было слишком медленно )
Такая мысль тоже была — либо предподсчитать, либо закешировать. В итоге синус и косинус я ускорил, а квадратный корень из квадрата координат (векторное расстояние) — закешировал.
Для корня из суммы квадратов есть линейное приближение с погрешностью не хуже 4%, может подойдет: https://youtu.be/NWBEA2ECX-A?si=j7rAWOfaS-eAD6NK
О, спасибо, попробую завтра!
Добавлено: Не выдержал, попробовал. В производительности разница в восемь раз, но будет ли достаточной точность для игры — непонятно, надо будет пробовать. Расхождение для некоторых вычисляемых значений у меня получилось ≈ 5,5, это как будто многовато.
Для графики я использовал таблицу синусов от 0 до 90 градусов с шагом 1 градус — то есть простая таблица из 90 строк позволяла вычислить любой синус\косинус за линейное время с точностью достаточной для мониторов тех лет (все равно синус с двумя знаками после запятой и синус с десятью знаками — попадут на один и тот же пиксел). Для современных мониторов шаг нужно увеличить до 0.1 градуса, наверное.
Да, можно закешировать вычисление, как я это сделал для квадратного корня.