Я профілював частину нашої основної математики на Intel Core Duo, і, переглядаючи різні підходи до квадратного кореня, я помітив щось дивне: використовуючи скалярні операції SSE, швидше взяти зворотний квадратний корінь і помножити його щоб отримати sqrt, ніж це використовувати рідний опкорд sqrt!
Я тестую його за допомогою циклу, наприклад:
inline float TestSqrtFunction( float in );
void TestFunc()
{
#define ARRAYSIZE 4096
#define NUMITERS 16386
float flIn[ ARRAYSIZE ]; // filled with random numbers ( 0 .. 2^22 )
float flOut [ ARRAYSIZE ]; // filled with 0 to force fetch into L1 cache
cyclecounter.Start();
for ( int i = 0 ; i < NUMITERS ; ++i )
for ( int j = 0 ; j < ARRAYSIZE ; ++j )
{
flOut[j] = TestSqrtFunction( flIn[j] );
// unrolling this loop makes no difference -- I tested it.
}
cyclecounter.Stop();
printf( "%d loops over %d floats took %.3f milliseconds",
NUMITERS, ARRAYSIZE, cyclecounter.Milliseconds() );
}
Я спробував це з декількома різними органами для TestSqrtFunction, і у мене з'явилися деякі терміни, які справді чухають мою голову. Найгірше, що на сьогоднішній день було використання натурної функції sqrt () і надання можливості «розумному» компілятору «оптимізувати». При 24ns / float, використовуючи x87 FPU, це було патетично погано:
inline float TestSqrtFunction( float in )
{ return sqrt(in); }
Наступне, що я спробував, - це використовувати внутрішнє слово, щоб змусити компілятор використовувати скалярний код кодування SST SSE:
inline void SSESqrt( float * restrict pOut, float * restrict pIn )
{
_mm_store_ss( pOut, _mm_sqrt_ss( _mm_load_ss( pIn ) ) );
// compiles to movss, sqrtss, movss
}
Це було краще, на рівні 11,9ns / float. Я також спробував хитрую техніку наближення Ньютона-Рафсона від Carmack, яка працювала навіть краще, ніж апаратне забезпечення, зі швидкістю 4,3ns / float, хоча з помилкою 1 на 2 10 (що занадто багато для моїх цілей).
Дузі було, коли я спробував SSE op для зворотного квадратного кореня, а потім використав множення, щоб отримати квадратний корінь (x * 1 / √x = √x). Хоча це вимагає двох залежних операцій, це було найшвидшим рішенням на сьогодні, зі швидкістю 1,24ns / float і з точністю до 2 -14 :
inline void SSESqrt_Recip_Times_X( float * restrict pOut, float * restrict pIn )
{
__m128 in = _mm_load_ss( pIn );
_mm_store_ss( pOut, _mm_mul_ss( in, _mm_rsqrt_ss( in ) ) );
// compiles to movss, movaps, rsqrtss, mulss, movss
}
Моє питання в основному що дає ? Чому вбудований до апаратури SSE квадратний кореневий кодовий опкод проходить повільніше, ніж синтезувати його з двох інших математичних операцій?
Я впевнений, що це дійсно вартість самої опції, тому що я перевірив:
- Усі дані вміщуються в кеш, а доступ є послідовним
- функції вбудовані
- розгортання циклу не має значення
- прапори компілятора встановлені на повну оптимізацію (і збірка хороша, я перевірив)
( відредагувати : stephentyrone правильно вказує, що для операцій над довгими рядками чисел слід використовувати векторизуючу SIMP-пакет, наприклад, rsqrtps
- але структура даних масиву тут призначена лише для тестування: те, що я насправді намагаюся вимірювати, - це скалярна ефективність для використання в коді це неможливо векторизувати.)
inline float SSESqrt( float restrict fIn ) { float fOut; _mm_store_ss( &fOut, _mm_sqrt_ss( _mm_load_ss( &fIn ) ) ); return fOut; }
. Але це погана ідея, оскільки вона може легко викликати стійло-завантаження магазину, якщо ЦП записує поплавці в стек, а потім негайно зчитує їх назад - жонглюючи з векторного регістра до плаваючого регістра для повернення значення, зокрема погана новина. Крім того, базові машини кодують, що представляють внутрішньосистеми SSE, все одно приймають операнди адреси.
eax
) дуже погано, в той час як обхід між xmm0 та стеком а назад - не через переадресацію магазину Intel. Ви можете встигнути самостійно, щоб точно побачити. Взагалі найпростіший спосіб побачити потенційний LHS - це переглянути випромінювану збірку і побачити, де дані переміщуються між наборами регістрів; ваш компілятор може зробити розумну справу, а може і не зробити. Що стосується нормалізації векторів, я написав свої результати тут: bit.ly/9W5zoU