Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

Sin Cos [Delphi]


Страницы:   1 2 3

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 даёт более точные результаты, чем глаз :)


Страницы:   1 2 3

Наверх

Титульная страница Поиск, карта сайта Написать письмо