![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Sin Cos [Delphi]
zxcxz © (2006-06-15 20:47:00)Слышал что функции sin и cos очень тормозные. В игровом приложении каждый кадр нуждается в вычислении большого количества значений от этих функций. Кто знает на сколько эти функции тормозят работу игрового приложения? И рациональнее ли заменить эти функции на выбор процедурой case of приблизительного значения из таблицы синусов и косинусов?
DevilDevil (2006-12-11 10:45:00) [1]> А не можешь пример SSE-кода?
Не можешь. Советую идти на www.wasm.ru
ors_archangel (2006-12-11 01:42:00) [2]
> DevilDevil © (10.12.06 21:24) [45]
Специально проверил, $o+, $a8, $r-, $q-, всё равно компилит вызов на вышеуказанную функцию, у меня Delphi 7 Enterprise build 4.453.
А не можешь пример SSE-кода?
DevilDevil (2006-12-10 21:24:00) [3]
> ведь cos = call @COS, который есть:
Уренда какая-то. Как я не исполльзовал, вызываются инлайнами. И уж точно не такой код (короче в разы)
> Кстати, пробовал, ужасно тормозит, видимо, что-то не так
> делаю! как их юзать то!!!
Чтобы добиться реального прироста производительности от SSE~, обработку всей последовательности данных в одной подпрограмме. Ещё там фишка с выравниванием данных; в этой подпрограмме как можно реже (а лучше вообще не) использовать обычные команды процессора.
ors_archangel (2006-12-10 12:33:00) [4]
> DevilDevil © (02.11.06 17:43) [35]
> функции sqr, sqrt, sin, cos, ... в любой версии Delphi
> вызываются инлайнами,
А вот я давно думаю о dcc (уж про v15 точно) крайне пессимистично, и часто правильно:
ведь cos = call @COS, который есть:
procedure _COS;
asm
FCOS
FNSTSW AX
SAHF
JP @@outOfRange
RET
@@outOfRange:
FSTP st(0) { for now, return 0. result would }
FLDZ { have little significance anyway }
end;
Кстати, ни у кого нет исходников компилера Делфи, хочу сделать там поиск слова optimization, вдруг найду :\
very fast!
> Сергей М. © (02.11.06 11:36) [25]
> приложения используют на полную мощь MMX/XMM/SSE/SSE2/SSE3/3DNow
Кстати, пробовал, ужасно тормозит, видимо, что-то не так делаю! как их юзать то!!! (пытался ускорить DotProduct, никто не делал через SSE2?)
p.s. Тестирование sin/cos таблиц нужно производить не на последовательное их вычисление, а в реальном алгоритме, т.к. так таблица у нас в кэше и всё при тесте, а в реале оно может не совсем так быть.
p.s.s. В DOOMе ещё удобно было, что углы были 0..255 и сами клиппились, удобно...
rts111 (2006-11-21 17:24:00) [5]> DevilDevil © (17.11.06 19:11) [38]
Не поленился, проверил :)
Работают такие варианты:
// если обявить TFLOAT_int = integer при вызове SinCos1( TFLOAT_int(v1) , v2 , v3 ) компилятор ругается
// А вот так работает:
type TFLOAT_int = pointer;
// !!! Только все равно нужно передавать не адрес, а значение, pointer - только потому
// чтобы Alpha в SinCos1 воспринимался как 32-витное значение
procedure SinCos1( const Alpha :TFLOAT_int; var Sin,Cos :Single ); register;
asm
// FLD EAX - если так, то не компилится
// можно так сделать
PUSH EAX
FLD DWORD PTR[ESP]
POP EAX
FSINCOS
FSTP DWORD PTR[ECX] // Cos
FSTP DWORD PTR[EDX] // Sin
end;
// Здесь компилятор сам добавляет pop...push, как я понял это потому что Alpha - вещественный тип
procedure SinCos2( const Alpha :single; var Sin,Cos :Single ); register;
asm
FLD Alpha
FSINCOS
FSTP DWORD PTR [EDX]
FSTP DWORD PTR [EAX]
end;
procedure SinCos3( var Alpha {EAX} , Sin {EDX} , Cos {ECX} :Single );
asm
FLD DWORD PTR[EAX]
FSINCOS
FSTP DWORD PTR[ECX] // Cos
FSTP DWORD PTR[EDX] // Sin
end;
procedure TForm1.Button1Click(Sender: TObject);
var
t0,t1,t2 :int64;
i :integer;
v1,v2,v3 :single;
begin
QueryPerformanceCounter(t1);
v1:=Pi/2;
for i:=1 to 1000000 do
SinCos1( TFLOAT_int(v1) , v2 , v3 ); // 1 место(самый быстрый)
//SinCos2( v1 , v2 , v3 ); // 3 место
//SinCos3( v1 , v2 , v3 ); // 2 место
QueryPerformanceCounter(t2);
//QueryPerformanceFrequency(t0);
caption:=inttostr(t2-t1)+' '+floattostr(v[2]);
end;
DevilDevil (2006-11-21 12:23:00) [6]> регистр сопроцессора
и
> регистр общего назначения
Чем отличаются?
Напиши, как ты вызываешь эту функцию и что примерно на это говорит дизассемблер.
rts111 (2006-11-20 20:22:00) [7]Это не глупость, непосредственно загрузить регистр сопроцессора
содержимым регистра общего назначения, насколько я знаю, нельзя.
DevilDevil (2006-11-20 16:49:00) [8]Выставь в опциях компилятора "Оптимизацию", может поможет... но верить в такую глупость компилятора мне, честно говоря, не хочется...
rts111 (2006-11-18 16:32:00) [9]>огорчу: в твоём случае происходит обращение к памяти по указателю, в моём - напрямую к
>регистру. Ну и что быстрее? Соглошусь: разница не велика.
Посмотри как компилятор откомилирует:
В твоем случае добавит pop ... push,
а в моем что записано, то будет и после компиляции.
DevilDevil (2006-11-17 19:11:00) [10]> rts111 © (11.11.06 13:33) [37]
> procedure SuperFastSinCos( var Theta, Sin, Cos :Single );
огорчу: в твоём случае происходит обращение к памяти по указателю, в моём - напрямую к регистру. Ну и что быстрее? Соглошусь: разница не велика.
rts111 (2006-11-11 13:33:00) [11]>DevilDevil © (02.11.06 17:43) [35]
...
> const Deg_To_Rad : double = (PI/180);
> type TFLOAT_int = integer;
>
> procedure FastSinCos(const Theta: TFLOAT_int; var Sin, Cos: single);
> asm
> FLD eax
> FMUL Deg_To_Rad // <- так, если не ошибаюсь
> FSINCOS
> FSTP dword ptr[ecx] // Cos
> FSTP dword ptr[edx] // Sin
> end;
Вот так должно работать еще быстрее:
procedure SuperFastSinCos( var Theta, Sin, Cos :Single );
asm
FLD DWORD PTR [EAX]
FMUL Deg_To_Rad
FSINCOS
FSTP DWORD PTR [ECX] // Cos
FSTP DWORD PTR [EDX] // Sin
end;
zxcxz (2006-11-11 05:58:00) [12]Ну, вы тему и развернули!
Я уже не надеялся ее увидеть на первых страницах форума.
for DeadMeat
> На оригинальность и супер скорость не претендую, но по моим
> тестам и для моей ситуации работает шустрее:
Идею понял и перенял, в моем случае прог летает.
Спасиб за идею!
Time(Sin & Cos) / Time(Your Sin & Cos) ~ 3.58
DevilDevil (2006-11-02 17:43:00) [13]КААЮЮСЬ !!!
const Deg_To_Rad : double = (PI/180);
type TFLOAT_int = integer;
procedure FastSinCos(const Theta: TFLOAT_int; var Sin, Cos: single);
asm
FLD eax
FMUL Deg_To_Rad // <- так, если не ошибаюсь
FSINCOS
FSTP dword ptr[ecx] // Cos
FSTP dword ptr[edx] // Sin
end;
Пример:
var Angle, sinAngle, cosAngle : single;
begin
Angle := random;
FastSinCos( TFLOAT_int(Angle), sinAngle, cosAngle );
Аналогично синус/косинус:
function FastCos (const X: TFLOAT_int): single;
asm
FLD X
FMUL Deg_To_Rad // <- так, если не ошибаюсь
FCOS
end;
function FastSin (const X: TFLOAT_int): single;
asm
FLD X
FMUL Deg_To_Rad // <- так, если не ошибаюсь
FSIN
end;
P.S. Но мне так кажется(не проверял), что обычный вызовsinAngle := sin(Deg_To_Rad*Angle);будет быстрее. "Почему?" - спросите вы (кроме Кетмара). Потому что функцииsqr, sqrt, sin, cos, ...в любой версии Delphi вызываются инлайнами, то есть не тратится лишнего времени на вызов функции как таковой. Всё
P.P.S. Ну а к Кетмару у меня и у некоторых других особое отношение...
DevilDevil (2006-11-02 17:27:00) [14]> Belorus © (02.11.06 16:41) [32]
> Я бы не стал так громко орать о том чего не знаю....
А чё орёшь тогда? :)
Если быть точным, то и ты неправ:
"... оно было реализовано в виде отдельной микросхемы, то есть его присутствие было необязательным. Микросхема сопроцессорадля процессора i8086/88имела название i8087. ... Как отдельные (...) устройства сопроцессоры сохранялись вплоть до модели i386 и имели название i287 и i387 соответственно. Начиная с модели i486 сопроцессор исполняется в одном корпусе с основным процессором..."
... но если я не ошибаюсь, команды сопроцессора достурны при программировании под 386...
Ketmar (2006-11-02 17:25:00) [15]>[32] Belorus(c) 2-Nov-2006, 16:41
>Я бы не стал так громко орать о том чего не знаю....
для данного персонажа это нормально.
Belorus (2006-11-02 16:41:00) [16]P.P.S Сопроцессор появился в 386 процессоре
Я бы не стал так громко орать о том чего не знаю....
По теме : в i486
DevilDevil (2006-11-02 15:51:00) [17]Нормальный способ:
procedure SinCos(const Theta: Extended; var Sin, Cos: Extended);
asm
FLD Theta
FSINCOS
FSTP tbyte ptr [edx] // Cos
FSTP tbyte ptr [eax] // Sin
end;
Убыстренный способ (если используешь переменные : single):
type TFLOAT_int = integer;
procedure FastSinCos(const Theta: TFLOAT_int; var Sin, Cos: TFLOAT_int);
asm
FLD eax
FSINCOS
FSTP ecx // Cos
FSTP edx // Sin
end;
Пример:
var Angle, sinAngle, cosAngle : single;
begin
Angle := random;
FastSinCos( TFLOAT_int(Angle), TFLOAT_int(sinAngle), TFLOAT_int(cosAngle) );
Ну и напоследок, если используешь градусы, то:
const Deg_To_Rad : double = (PI/180);
procedure FastSinCos(const Theta: TFLOAT_int; var Sin, Cos: TFLOAT_int);
asm
FLD eax
FMUL Deg_To_Rad // <- так, если не ошибаюсь
FSINCOS
FSTP ecx // Cos
FSTP edx // Sin
end;
<-- по аналогии (и даже проще) найдёшь только синус или только косинус, если не нужны оба значения
P.S. Таблицадаст прирост скорости, если:
1) выборка будет inline
2) не будешь "вручную" переводить радианы в градусы
3) не будешь округлять угол "до ближайшего целого"
P.P.S Сопроцессор появился в 386 процессоре
Сергей М. (2006-11-02 14:11:00) [18]SIMD "спасает отцов русской демократии")
Shirson (2006-11-02 13:17:00) [19]Так.
Сделал таблицу на 3'600'000 предзначений.
Делается 10000001 выборок а) из таблицы и б) считая синус функцией.
а) Среднее количество тиков для функции - 1734
б) Среднее количество тиков для таблицы - 328
Shirson (2006-11-02 12:47:00) [20]Такие вещи "на глаз" не делаются. GetTickCount даёт более точные результаты, чем глаз :)