Теперь функция получила 45393 пункта, и производительность изменилась на 3%. FXCH действительно ни причем, поскольку производительность опять ушла вниз. В чем же дело?
Инструкция WAIT была рассмотрена в более раннем уроке, и в данный момент мы просто удалим ее.
Code: |
function ArcSinApprox1j(X, A, B, C, D : Double) : Double; asm //Result := A*X*X*X + B*X*X + C*X + D; fld qword ptr [esp+$28] fld qword ptr [esp+$20] fmul st(0),st(1) fmul st(0),st(1) fmul st(0),st(1) fld qword ptr [esp+$18] fmul st(0),st(2) fmul st(0),st(2) faddp fld qword ptr [esp+$10] fmul st(0),st(2) ffree st(2) faddp fadd qword ptr [esp+$08] //wait end; |
Производительно упала до 44140.
Посмотрим эти удивляющие нас результаты на процессоре P3.
ArcSinApprox1a 63613
ArcSinApprox1b 64412
ArcSinApprox1c 64433
ArcSinApprox1d 65062
ArcSinApprox1e 64830
ArcSinApprox1f 62598
ArcSinApprox1g 79586
ArcSinApprox1h 85361
ArcSinApprox1i 80515
ArcSinApprox1j 80192
Во-первых, видим, что вариант ArcSinApprox1h самый быстрый на P3. Поэтому видно, что загрузка данных из кэш памяти L1 более ощутима на P3, чем на P4, поскольку изменение кода, такое как одноразовая загрузка X дало существенное улучшение производительности на P3, и почти нет на P4. С другой стороны мы можем также сказать, что получение данных из кэш памяти всегда медленнее, чем получение из внутренних регистров. P4 имеет быструю кэш память уровня L1, которая читается только за 2 такта, но внутренние регистры еще быстрее, только один такт. Мы также видим, что P3 на частоте 1400 примерно на 80% быстрее, чем P4 на частоте 1920 в данном коде. Мы знаем, что латентность на P3 короче, но этого недостаточно для объяснения такой большой разницы.
Латентность и ускорение (throughput) по использованным регистрам на P3
FADD latency is 3 clock cycles and throughput is 1
FMUL latency is 5 clock cycles and throughput is 1
На P4
FADD latency is 5 clock cycles and throughput is 1
FMUL latency is 7 clock cycles and throughput is 2
Я не смог найти данных для FLD
Объяснение плохой производительности P4 в данном коде состоит в 2-тактном сквозном проходе по конвейеру (throughput) для FMUL, совместно с медленным доступом до FP регистров процессора. Конвейер FMUL получает доступ до следующей инструкции только за два такта, тогда как P3 за один такт.
Нормализованный к частоте результат
47939 / 1920 = 25
85361 / 1400 = 61
разоблачает, что при приведении частот процессор P3 примерно в 2.5 раза быстрее P4. Это вызывает подлинное удивление. Чтобы P4 имел некоторые шансы, по отношению к P 3, нам мы должны убрать некоторые умножения. Это получается в функции по версии Хорнера.
Code: |
function ArcSinApprox3a(X, A, B, C, D : Double) : Double; begin Result := ((A*X + B)*X + C)*X + D; end; |
Это компилируется в
Code: |
function ArcSinApprox3b(X, A, B, C, D : Double) : Double; begin { push ebp mov ebp,esp add esp,-$08 } Result := ((A*X + B)*X + C)*X + D; { fld qword ptr [ebp+$20] fmul qword ptr [ebp+$28] fadd qword ptr [ebp+$18] fmul qword ptr [ebp+$28] fadd qword ptr [ebp+$10] fmul qword ptr [ebp+$28] fadd qword ptr [ebp+$08] fstp qword ptr [ebp-$08] wait fld qword ptr [ebp-$08] } { pop ecx pop ecx pop ebp } end; |
Первые три версии этой функции идентичны и они получают свои очки без сюрпризов. Наша методика измерения не совсем хороша, но дает достаточную точность в текущий момент ;-)
ArcSinApprox3a 45076
ArcSinApprox3b 45076
ArcSinApprox3c 45076
Оптимизация следует по тому же шаблону, как и в первой функции. Вот первая BASM версия без оптимизации. Закомментирован код добавленный компилятором.
Code: |
function ArcSinApprox3c(X, A, B, C, D : Double) : Double; asm //push ebp //mov ebp,esp add esp,-$08 //Result := ((A*X + B)*X + C)*X + D; fld qword ptr [ebp+$20] fmul qword ptr [ebp+$28] fadd qword ptr [ebp+$18] fmul qword ptr [ebp+$28] fadd qword ptr [ebp+$10] fmul qword ptr [ebp+$28] fadd qword ptr [ebp+$08] fstp qword ptr [ebp-$08] wait fld qword ptr [ebp-$08] pop ecx pop ecx //pop ebp end; |
Первым делом удаляем строку ADD ESP, -$08 и две строки POP ECX. Они устанавливают фрейм стека, но ничего не делают кроме манипулирования указателем стека, который нигде не используется.
Code: |
function ArcSinApprox3d(X, A, B, C, D : Double) : Double; asm //add esp,-$08 //Result := ((A*X + B)*X + C)*X + D; fld qword ptr [ebp+$20] fmul qword ptr [ebp+$28] fadd qword ptr [ebp+$18] fmul qword ptr [ebp+$28] fadd qword ptr [ebp+$10] fmul qword ptr [ebp+$28] fadd qword ptr [ebp+$08] fstp qword ptr [ebp-$08] wait fld qword ptr [ebp-$08] //pop ecx //pop ecx end; |
Данная функция получила 43535 пункта.
Обе лишние строки, копирующие результат на стек и обратно, удалены одновременно.
Code: |
function ArcSinApprox3e(X, A, B, C, D : Double) : Double; asm //Result := ((A*X + B)*X + C)*X + D; fld qword ptr [ebp+$20] fmul qword ptr [ebp+$28] fadd qword ptr [ebp+$18] fmul qword ptr [ebp+$28] fadd qword ptr [ebp+$10] fmul qword ptr [ebp+$28] fadd qword ptr [ebp+$08] //fstp qword ptr [ebp-$08] wait //fld qword ptr [ebp-$08] end; |
Этот вариант получил 47237 пункта, и улучшение составило 8.5%
Просьба писать ваши замечания, наблюдения и все остальное,
что поможет улучшить предоставляемую информацию на этом сайте.
ВСЕ КОММЕНТАРИИ МОДЕРИРУЮТСЯ ВРУЧНУЮ, ТАК ЧТО СПАМИТЬ БЕСПОЛЕЗНО!