Войти
ПрограммированиеФорумОбщее

Быстрые и точные тригонометрические функции (3 стр)

Advanced: Тема повышенной сложности или важная.

Страницы: 1 2 3 4 5 6 Следующая »
#30
10:18, 25 мар. 2009

Погонял в своих тестах на double на G++ и выводы интересные получаются.
Асм-код функции COS0 получается такой же как и у автора топика.
G++ в любых режимах оптимизации стандартный cos() представляет как call cos.
Ключи компилятора такие:
g++ -march=athlon64 -msse2 -mfpmath=sse -masm=intel -OX main.cpp

При -O2 имеет место как call cos, так и call COS0, результаты не очень впечатляют:
cos - 0.86 секунд
COS0 - 0.70 секунд
Т.е. 22% выигрыша. В принципе неплохо, но что с точностью? Отличие в десятичной системе проявляется только в 15-16-ых знаках. В принципе отлично, учитывая что именно это число знаков примерно и является числом значащих цифр!
В моём тесте делается следующее - суммируются косинусы от 0 до 2Pi с шагом 0.01 и эти результаты в цикле суммируются еще 100 раз. Получившиеся суммы у cos и COS0 вот такие:
68.1469938554409...
68.1469938554967...
Т.е. отличие начинается в 13-ом знаке, и это при таком то числе сумм!

Поинтереснее режим -O3. В нём cos() остаётся call cos(), а вот COS0 инлайнится и за счет этого:
cos - 0.82 секунд
COS0 - 0.38 секунд
Т.е. чуть больше чем в 2 раза быстрее. Это уже интереснее и практичнее... Тот же результат будет у -O2, если функцию явно объявить inline.
Т.е. в каких-то критических моментах очень даже полезный финт получается.

Автору плюс.


#31
11:00, 25 мар. 2009

Suslik

Приведенный тобой ряд тейлора действительно раза в 1.5 быстрее COS0, но начинает сильно расходится по сравнению с ним при увеличении аргумента. При аргументе PI точность теряется уже на 9-ом знаке. При 2 * PI уже на четвертом.
COS0 же - стабильно 14-15 знаков точности на всём диапазоне 0..2Pi.

#32
11:43, 25 мар. 2009

=A=L=X=

Интересно сравнение с cos на msvc, где он при оптимизации всегда инлайнится. Попробуй еще сравнить с самописной:

inline double fpu_cos(const double a)
{
  __asm
  {
    fld a
    fcos
  }
}

#33
11:47, 25 мар. 2009

Ghost2

>Попробуй еще сравнить с самописной:

С самописной хотелось бы сравнить, да вот не знаю синтаксис AT&T ассемблера, который в G++ применяется. :)
Надо глянуть в инринсинки, мож там такое есть...

P.S.

Нет. Интел интринсинками изначально считает сами cos/sin из math.h даже без каких-либо прочих приблуд... G++ к сожалению не таков почему-то...

#34
11:55, 25 мар. 2009

asvp
Напиши, пожалуйста, и синус. Хочу проверить скорость и точность.

#35
14:16, 25 мар. 2009

Доброго дня!

=A=L=X=
По поводу топа #30. Все врено. В режиме оптимизации в Intel C++ функция COS тоже вызывается так:
10002CFB  movsd      xmm0,mmword ptr [a]
10002D03  call        ___libm_sse2_cos (10010B60h)
10002D08  movsd      mmword ptr [ValueCos],xmm0

По поводу топа #31.
С этим я и столкнулся. Необходимо было для точности расчета выполнять подгон угла к интервалу. И возникла идея найти полином для интервала от 0 до 2PI.
Т.к. функции цикличны, то более большого интервала не требуется. А подогнать к интервалу от 0 до 2PI гораздо проще. Потом уже определил, что значения достоверны и на интервале от -2PI до 2PI.

Ghost2
По поводу топа #32.
Ты забываешь вернуть результат.

Не забывайте float и double разные типы данных. С разными командами и временем выполнения операций, с точностью предстваления, с накоплением ошибок при вычислениях.
Для себя я решил, что:
1. Мин. float = 1.192092896e-07f (#define FLT_EPSILON    1.192092896e-07F        /* smallest such that 1.0+FLT_EPSILON != 1.0 */ из файла float.h)
2. Мин. double = 2.2204460492503131e-016 (#define DBL_EPSILON    2.2204460492503131e-016 /* smallest such that 1.0+DBL_EPSILON != 1.0 */ из файла float.h)
3. Все, что меньше данных чисел (для каждого типа соотвественно), считаю 0.

Таймер, которым я пользуюсь.
http://www.gamedev.ru/download/?id=8651
Часть основного кода тестирования.
 
  PBL_ALIGN(16) F64 MaxTimeCos=0.0, MaxTimeFastCos=0.0, TimeCos, TimeFastCos, ValueCos, ValueFastCos;
  char Buffer[1024];
   
  ::SetProcessAffinityMask(::GetCurrentProcess(), 1);
  ::SetPriorityClass(::GetCurrentProcess(), REALTIME_PRIORITY_CLASS);
  ::SetThreadPriority(::GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
 
  for (a=0; a<=_dTwoPI; a+=1.0e-5)
  {
    CTIMER::Reset();
    ValueCos=COS(a);
    ValueFastCos=SIN0(a);
    TimeCos=CTIMER::GetDeltaTime();
    if (MaxTimeCos<TimeCos) MaxTimeCos=TimeCos;
  }
  for (a=0; a<=_dTwoPI; a+=1.0e-5)
  {
    CTIMER::Reset();
    ValueFastCos=COS0(a);
    TimeFastCos=CTIMER::GetDeltaTime();
    if (MaxTimeFastCos<TimeFastCos) MaxTimeFastCos=TimeFastCos;
  }
  sprintf(Buffer, "MaxTimeCos: %0.17lf\rMaxTimeFastCos: %0.17lf",  MaxTimeCos, MaxTimeFastCos);
  ::MessageBox(NULL, Buffer, NULL, MB_OK);

Прогон 1:
COS -  0.12794922259670743 мс.
COS0 - 0.12264128541469976 мс.
Прогон 2:
COS -  0.16482541775559334 мс.
COS0 - 0.12347938075907039 мс.
Прогон 3:
COS -  0.79144137034176143 мс.
COS0 - 0.12347938075919207 мс.
Вывод: Функция COS0 выполняется за меньшее или равное время от выполнения стандартной функции COS.

Предварительно расчитанный полином SIN 26 степени.

PBL_INLINE F64 SIN0(const F64& v)
{
  PBL_ALIGN(16) const F64 Sq(v*v);
  return (((((((((((((4.412897335796758e-26 * Sq)-
          3.644260218273361e-23) * Sq+
          1.9424465862206958e-20)* Sq-
          8.21418047933637e-18)  * Sq+
          2.8112657423118235e-15)* Sq-
          7.647124347003046e-13) * Sq+
          1.605903823489783e-10) * Sq-
          2.505210784443164e-8)  * Sq+
          2.7557319189814762e-6) * Sq-
          0.00019841269839946478)* Sq+
          0.008333333333305163)  * Sq-
          0.1666666666666393)    * Sq+
          0.99999999999999)      * v;
}

Прогон 1:
SIN -  0.14331430391293187 мс.
SIN0 - 0.12180319007029672 мс.
Прогон 2:
SIN -  0.80596835631343422 мс.
SIN0 - 0.12124445984055088 мс.
Прогон 3:
SIN -  0.12320001564445915 мс.
SIN0 - 0.12739049236702160 мс.

Тестировал на Core2Duo E8400.

#36
14:46, 25 мар. 2009

asvp

Выложи свой тест лучше.

>Ты забываешь вернуть результат.
Ничего я не забываю.

#37
14:49, 25 мар. 2009

=A=L=X=
>С самописной хотелось бы сравнить, да вот не знаю синтаксис AT&T ассемблера, который в G++ применяется. :)

__inline__ double fpu_cos(double arg)
{
    double res;
    __asm__("fcosl" : "=st"(res) : "0"(arg));
    return res;
}
#38
14:56, 25 мар. 2009

asvp

>10002D03 call ___libm_sse2_cos (10010B60h)
Ололо. Может выключим на секунду SSE?

#39
15:01, 25 мар. 2009

Ghost2
Если честно мне по барабану на эти тесты. Я для себя тестирую и мне достаточно. А как вы будете тестировать меня не волнует. Писать какие-то спец тесты влом.
Одним словом пробуй сам разные тесты и методы теста. Бери пример с =A=L=X=. Он и в другой системе тестил да и еще в разных режимах. Молодца.
>Ничего я не забываю.
Топ #37 или так

inline double fpu_cos(const double a)
{
  double res;
  __asm
  {
    fld a
    fcos
    fstp res
  }
  return res;
}

>Ололо. Может выключим на секунду SSE?
В VS2008 + Intel C++ не все так просто. Видимо разработчики считаю, что процессоров не подерживающих SSE и SSE2 уже нет и поэтому по умолчанию комилят с поддержкой SSE и SSE2. Но попробую.

#40
15:13, 25 мар. 2009

asvp

Вот асм листинг моего варианта (уже заинлайненного):

00401013  fld         qword ptr [esp+38h] 
00401017  fcos
00401019  sub         esp,8 
0040101C  fstp        qword ptr [esp]

А вот твоего:

00401013  fld         qword ptr [esp+38h] 
00401017  fcos
00401019  fstp        qword ptr [esp+30h] 
0040101D  fld         qword ptr [esp+30h] 
00401021  sub         esp,8 
00401024  fstp        qword ptr [esp] 

В твоем два раза fstp и один лишний load.

>Если честно мне по барабану на эти тесты.
Зачем было тогда тему создавать? Тесты в одиночку это такая штука, которая лучше всего помогает обмануть себя.
Короче: show tests or gtfo

#41
15:26, 25 мар. 2009

Ghost2
Попробывал использовать твою функцию компилятор выругался:
warning #1011: missing return statement at end of non-void function "pbl3d::core::fpu_cos"
Типа нет возрата результат из функции. Мне вот интересно как ты компил и чем?

А вот то, что уменя скомпилировалось при исправлении (заинлайнен):
  63:    ValueCos=fpu_cos(a);
10002CF7  movsd      xmm0,mmword ptr [a]
10002CFF  movsd      mmword ptr [ebp-278h],xmm0
10002D07  fld        qword ptr [ebp-278h]
10002D0D  fcos           
10002D0F  fstp        qword ptr [ebp-280h]
10002D15  movsd      xmm0,mmword ptr [ebp-280h]
10002D1D  movsd      mmword ptr [ValueCos],xmm0
    64:    TimeCos=CTIMER::GetDeltaTime();

>Зачем было тогда тему создавать? Тесты в одиночку это такая штука, которая лучше всего помогает обмануть себя.
Я выкладовал алгоритм. А не тест алгоритма. А уж пользоваться алогоритмом это твое дело. На то ты и программер. Но перед тем как его использовать, ты же будешь его тестить. Так вот как и чем мне не интересно. А вот результаты - да.

#42
15:36, 25 мар. 2009

asvp

Компилил vc80 и vc90. fstp вставляется автоматически.

>А вот то, что уменя скомпилировалось при исправлении (заинлайнен):
Судя по листингу, весь performance убит миксом fpu и sse.

#43
15:40, 25 мар. 2009

Ghost2

> Судя по листингу, весь performance убит миксом fpu и sse.

Не должно бы. В G++ даже опция есть "использовать для математики и SSE и FPU" для лучшего распараллеливания.

#44
15:43, 25 мар. 2009

Ghost2
>Компилил vc80 и vc90. fstp вставляется автоматически.
Даже предупреждение не дал? Странно?
>А вот то, что уменя скомпилировалось при исправлении (заинлайнен):
>Судя по листингу, весь performance убит миксом fpu и sse.
А ты что хотел получить. То, что написано в блоке _asm {...} компилится как есть.

Страницы: 1 2 3 4 5 6 Следующая »
ПрограммированиеФорумОбщее

Тема в архиве.