PDA

Просмотр полной версии : [Function] int_sqrt



SooBad
01.05.2017, 16:29
Название: int_sqrt

Нотация: самописная функция, предназначенная для вычисления корня у значений целочисленного типа. Аналог нативного floatsqroot и си-шного sqrt.

Производительность: при 10k итераций тесты возвращают результат, равный 0-2 ms.

Плюсы реализации:
• При написании независимых систем вам не придётся подгружать библиотеку float для подсчётов, с применением лишь одной функции.
• Не требует преобразований целочисленных значений в вещественные, в отличие от того же floatsqroot.

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

Пример использования(работоспособность):

main()
{
new
test_value = 250;

printf("%d", int_sqrt(test_value + 5));

//result = 15
}

Исходный код:

stock int_sqrt(value)
{
new
result,
temp,
degr_count;

for(new i = 3; i >= 0; i--)
{
temp = degr_count |= 0b11 << (0b10 * i) & value;

returning:
result ^= 0b1 << i;

if(temp < (result * result))
goto returning;
}
return result;
}

Автор: SooBad.

DeimoS
01.05.2017, 16:45
• При написании независимых систем вам не придётся подгружать библиотеку float для подсчётов, с применением лишь одной функции.


Так можно ведь просто прописать саму нативку для floatsqroot, а не грузить всю библиотеку, которая, тащемта, из одних нативок, в основном, и состоит :)



P.S. Что за проблемы с табуляцией?

Скорее всего просто табы перемешаны с пробелами были. В редакторах это не заметно, а вот на форумах разница видна.
Это проблема не только данного форума, если что

SooBad
01.05.2017, 17:07
Так можно ведь просто прописать саму нативку для floatsqroot, а не грузить всю библиотеку, которая, тащемта, из одних нативок, в основном, и состоит :)

Да, можно. Я уже по ходу реализации об этом подумал.
Но, плюсы от функции всё равно определенные есть. Насколько я знаю, когда мы в floatsqroot, да и в других float-функциях работаем с целыми числами, то компилятор выплевывает предупреждение о потере тега. Проверить сейчас это нет возможности.



Скорее всего просто табы перемешаны с пробелами были. В редакторах это не заметно, а вот на форумах разница видна.

Да, скорее всего, так и есть.

Nexius_Tailer
01.05.2017, 18:49
Производительность: при 10k итераций тесты возвращают результат, равный 0-2 ms.
Объективнее сравнивать с чем-либо, потому что мощности железа у каждого разные и замеры одной функции на одной машине вряд ли кому-то дадут конкретную информацию.

А так, хорошо бы ещё было увидеть как раз библиотеку с аналогами из float.inc, но для целочисленных переменных, вроде abs(olute), power, log(arithm) и т.д.

SooBad
01.05.2017, 19:12
Объективнее сравнивать с чем-либо, потому что мощности железа у каждого разные и замеры одной функции на одной машине вряд ли кому-то дадут конкретную информацию.


Сравнения происходили с аналоговым нативом. При 10k был возвращен тиковый результат, колеблющийся от 0 до 2 млс.
При 100k значения незначительно, но стали разниться, в пользу натива. 3 против 13. Тестировать на больших количествах итераций - смысла не вижу. Разницы, фактически, никакой.



А так, хорошо бы ещё было увидеть как раз библиотеку с аналогами из float.inc, но для целочисленных переменных, вроде abs(olute), power, log(arithm) и т.д.


Хорошая идея. Думаю, в ближайшем времени, реализую.

Daniel_Cortez
03.05.2017, 08:49
degr_count |= 0b11 << (0b10 * i);

#emit load.s.pri value
#emit load.s.alt degr_count
#emit and
#emit stor.s.pri temp

returning:
result ^= 0b1 << i;

В чём профит использовать #emit таким образом? Компилятор и без этого сгенерирует точно такую же последовательность инструкций.


main()
{
new a, b, c;
c = a & b;
#pragma unused c
}



proc ; main
; line 6
; line 7
;$lcl a fffffffc
push.c 0
;$exp
;$lcl b fffffff8
push.c 0
;$exp
;$lcl c fffffff4
push.c 0
;$exp
; line 8

load.s.pri fffffff8
load.s.alt fffffffc
and
stor.s.pri fffffff4

;$exp
stack c
zero.pri
retn

SooBad
03.05.2017, 17:26
В чём профит использовать #emit таким образом? Компилятор и без этого сгенерирует точно такую же последовательность инструкций.


main()
{
new a, b, c;
c = a & b;
#pragma unused c
}



proc ; main
; line 6
; line 7
;$lcl a fffffffc
push.c 0
;$exp
;$lcl b fffffff8
push.c 0
;$exp
;$lcl c fffffff4
push.c 0
;$exp
; line 8

load.s.pri fffffff8
load.s.alt fffffffc
and
stor.s.pri fffffff4

;$exp
stack c
zero.pri
retn


Да, разницы никакой нет. Это вполне очевидно.
Всё упирается лишь в восприятие кода и совместимости. (Некоторые не знают про данный оператор, либо используют pawn 4.x).
Поэтому, тут дело случая. Опасность применения вышеуказанных инструкций отсутствует.
Кстати, по просьбе Nexius, скорее всего, всё будет преобразовано в единую библиотеку (с ещё несколькими добавленными функциями).

Daniel_Cortez
03.05.2017, 18:38
Да, разницы никакой нет. Это вполне очевидно.
Всё упирается лишь в восприятие кода и совместимости. (Некоторые не знают про данный оператор, либо используют pawn 4.x).
Поэтому, тут дело случая. Опасность применения вышеуказанных инструкций отсутствует.
Равно как и отсутствует хоть мало-мальски внятная причина обфусцировать свой код. Зато проглядывается намерение устроить показуху.

Справедливости ради стоит отметить, что возможность оптимизировать код с помощью #emit есть, но она совсем небольшая: если преобразовать выражение "degr_count |= 0b11 << (0b10 * i);", то в последующем коде "temp = value & degr_count" можно избежать лишнюю загрузку значения из degr_count в один из регистров.


Также хотелось бы отдельно указать на "плюсы" реализации, которые тоже очень и очень сомнительны.


Не требует преобразований целочисленных значений в вещественные, в отличие от того же floatsqroot.
Если вы видите в этом препятствие производительности, то функция int_sqrt потребует ещё больше ресурсов.
Чтобы не быть голословным, предоставлю тест производительности.

Профайлер: http://pro-pawn.ru/showthread.php?12585

Настройки:


/*======== Настройки =========================================================*/
const PROFILER_ITERATIONS_MAJOR = 10_000;
const PROFILER_ITERATIONS_MINOR = 100;

new const code_snippets_names[2][] =
{
{"int_sqrt"},
{"float+floatsqroot+floatround"}
};

stock int_sqrt(value)
{
new
result,
temp,
degr_count;

for(new i = 3; i >= 0; i--)
{
degr_count |= 0b11 << (0b10 * i);

#emit load.s.pri value
#emit load.s.alt degr_count
#emit and
#emit stor.s.pri temp

returning:
result ^= 0b1 << i;

if(temp < (result * result))
goto returning;
}
return result;
}

#define Prerequisites();\
static i;
const TEST_MIN_VALUE = 1, TEST_MAX_VALUE = 100, TEST_STEP = 5;

#define CodeSnippet0();\
for (i = TEST_MIN_VALUE; i <= TEST_MAX_VALUE; i += TEST_STEP)\
int_sqrt(i);

#define CodeSnippet1();\
for (i = TEST_MIN_VALUE; i <= TEST_MAX_VALUE; i += TEST_STEP)\
floatround(floatsqroot(float(i)), floatround_tozero);
/*======== Конец настроек ===================================================*/


Вывод:


Тестирование: <int_sqrt> vs <float+floatsqroot+floatround>
Режим: интерпретируемый, 10000x100 итераций.
int_sqrt: 15672
float+floatsqroot+floatround: 1708



Тестирование: <int_sqrt> vs <float+floatsqroot+floatround>
Режим: с JIT-компиляцией, 10000x100 итераций.
int_sqrt: 1365
float+floatsqroot+floatround: 880


Связка "float+floatsqroot+floatround" требует вызова трёх нативных функций, но затраты окупаются за счёт быстрого вычисления корня математическим сопроцессором, что ничуть не удивительно: он специально создан для подобных задач.



При написании независимых систем вам не придётся подгружать библиотеку float для подсчётов, с применением лишь одной функции.
"Независимых систем"? Кому в здравом уме нужна будет независимость от библиотеки float?
У неё слишком обширный набор функций? Нет, в ней только основные функции для работы с вещ. числами. Возможно, этих функций там даже слишком мало - не хватает обратных тригонометрических функций, которые реализованы отдельно в SA-MP.
Ок, может быть это чья-то сторонняя библиотека, которую не хотелось бы лишних раз таскать за собой? Нет, это один из стандартных модулей Pawn AMX.
Тогда, может модуль amxfloat специфичен для какой-то программно-аппаратной платформы и имеет проблемы с переносимостью? И снова мимо. Это всего лишь вещественные числа, которые есть практически на любой аппаратной платформе (кроме, разве что, каких-то малоизвестных и узкоспециализированных), а потому можно даже не задумываться о переносимости модуля - он портируется так же просто, как и остальные. К слову, куда больше проблем с портированием может возникнуть у модулей amxfile, amxcons и amxdgram (неполноценная стандартная библиотека C, нетривиальный API для работы с консолью, etc.), так что amxfloat здесь, наоборот, самый простой.

Единственное удобство int_sqrt, которое мне приходит в голову, так это более короткая запись. Такое себе преимущество, поскольку оно легко достигается с помощью макросов.


#define int_sqrt(%0)\
floatround(floatsqroot(float(%0)), floatround_tozero)

SooBad
05.05.2017, 14:54
Справедливости ради стоит отметить, что возможность оптимизировать код с помощью #emit есть, но она совсем небольшая: если преобразовать выражение "degr_count |= 0b11 << (0b10 * i);", то в последующем коде "temp = value & degr_count" можно избежать лишнюю загрузку значения из degr_count в один из регистров.

Читабельность это никак не снижает. Загрузка в регистры - это самая что не наесть маломальская затрата, которую только можно вызвать. Считанные микросекунды.
Разница между прямой генерацией запроса и задействования побитовой операции - 10-20 млс(на большом кол-ве итераций). Совершенно несущественно.



Если вы видите в этом препятствие производительности, то функция int_sqrt потребует ещё больше ресурсов.
Чтобы не быть голословным, предоставлю тест производительности.


Увы, но более скоростного варианта реализации я не придумал. Буду рад, если что-то в этом роде будет предложено.


Возможно, этих функций там даже слишком мало - не хватает обратных тригонометрических функций, которые реализованы отдельно в SA-MP.

Тригонометрия совершенно не нужна в сфере сампа. Ибо никаких возможностей по созданию собственных граф.движков и схожего с этим - нету. Иногда они задействуются для более точного вычисления угла поворота персонажа, но это не обратные тригонометрические функции.

Daniel_Cortez
05.05.2017, 17:20
Читабельность это никак не снижает.
Скажите это большинству пользователей, которые, как правило, не знают #emit. Пускай посмеются.

Честно говоря, я и сам не сразу понял, что именно делает эта ассемблерная вставка. Но не потому, что не знаю набора инструкций, а по причине того, что не смог понять, для чего вообще нужно было раскладывать на #emit такой простой код (серьёзно, всего 1 строка). Может вы посветите меня и других участников форума в причины столь странного решения? Ибо пока что ваш аргумент выглядит как отговорка в стиле "этот недостаток не влияет на работу, так что я его оставлю".



Разница между прямой генерацией запроса и задействования побитовой операции - 10-20 млс(на большом кол-ве итераций). Совершенно несущественно.
"Генерацией запроса"? О чём вы? Мы же не с базой данных работаем... или я что-то пропустил?



Увы, но более скоростного варианта реализации я не придумал. Буду рад, если что-то в этом роде будет предложено.
В моём посте выше уже предложен вариант. И это если не считать ещё одного, который описан устно.



Тригонометрия совершенно не нужна в сфере сампа.
Если она не нужна вам, то это ещё не значит, что она не нужна никому.



Ибо никаких возможностей по созданию собственных граф.движков и схожего с этим - нету.
Ничто не мешает взять один из open source графических движков или даже "голое" API типа OpenGL и добавить привязку к Pawn. Либо можно зайти дальше и сделать программный рендеринг, если нужна минимальная зависимость от графического стека.



Иногда они задействуются для более точного вычисления угла поворота персонажа, но это не обратные тригонометрические функции.
Как на счёт арктангенса (https://ru.wikipedia.org/wiki/%D0%9E%D0%B1%D1%80%D0%B0%D1%82%D0%BD%D1%8B%D0%B5_%D1%82%D1%80%D0%B8%D0%B3%D0%BE%D0%BD%D0%BE%D0%BC%D0%B5%D1%82%D1%80%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B5_%D1%84%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D0%B8), например?

http://pro-pawn.ru/showthread.php?12246
http://pro-pawn.ru/showthread.php?12711

SooBad
05.05.2017, 17:53
Скажите это большинству пользователей, которые, как правило, не знают #emit. Пускай посмеются.

Честно говоря, я и сам не сразу понял, что именно делает эта ассемблерная вставка. Но не потому, что не знаю набора инструкций, а по причине того, что не смог понять, для чего вообще нужно было раскладывать на #emit такой простой код (серьёзно, всего 1 строка).


Ок, вы меня переубедили. Обновил пост.



"Генерацией запроса"? О чём вы? Мы же не с базой данных работаем... или я что-то пропустил?


Инструкций*



Как на счёт арктангенса (https://ru.wikipedia.org/wiki/%D0%9E%D0%B1%D1%80%D0%B0%D1%82%D0%BD%D1%8B%D0%B5_%D1%82%D1%80%D0%B8%D0%B3%D0%BE%D0%BD%D0%BE%D0%BC%D0%B5%D1%82%D1%80%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B5_%D1%84%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D0%B8), например?

http://pro-pawn.ru/showthread.php?12246
http://pro-pawn.ru/showthread.php?12711


Однако, случаи задействования тригонометрии можно исчислять считанными единицами.
Некоторые такие подсчёты можно провести даже вручную.

ziggi
05.05.2017, 19:40
Однако, случаи задействования тригонометрии можно исчислять считанными единицами.
Некоторые такие подсчёты можно провести даже вручную.

Вот здесь есть очень много хороших примеров, как можно применить тригонометрию в SA-MP: http://forum.sa-mp.com/showthread.php?t=591010

VVWVV
08.05.2017, 00:17
#define int_sqrt(%0)\
floatround(floatsqroot(float(%0)), floatround_tozero)


Это же препроцессор, а значит мы можем использовать разнообразные схемы для того, чтобы конвертировать обычное число в вещественное, например:


#define FC:%0\32; FC:%0
#define int_sqrt(%0) floatround(floatsqroot(Float:FC:%0.0), floatround_tozero)

Таким образом, получаем пару мс.

Daniel_Cortez
08.05.2017, 01:26
Это же препроцессор, а значит мы можем использовать разнообразные схемы для того, чтобы конвертировать обычное число в вещественное, например:


#define FC:%0\32; FC:%0
#define int_sqrt(%0) floatround(floatsqroot(Float:FC:%0.0), floatround_tozero)

Таким образом, получаем пару мс.
С переменными не прокатит же. Равно как и со всем остальным (результатами вызова функций, выражениями, etc.), кроме целых чисел, записанных именно как числа, а не идентификатор или что-то ещё.

VVWVV
08.05.2017, 01:31
С переменными не прокатит же.

ах, да, переменные... Думаю, можно сделать фильтрацию на символы, поскольку название переменной не может начинаться с цифры.

Daniel_Cortez
08.05.2017, 02:03
ах, да, переменные... Думаю, можно сделать фильтрацию на символы, поскольку название переменной не может начинаться с цифры.
Я обновил пост выше, пока ты писал ответ. В любом случае, фильтрация символов тоже не прокатит против выражений.

VVWVV
08.05.2017, 02:37
В любом случае, фильтрация символов тоже не прокатит против выражений.

Почему же? Мы же будем фильтровать только цифры, а всё остальное передавать с функцией float().