Содержание материала

 

Удаляем фрейм стека и две строки, которые пишут результат на стек

Code:

function ArcSinApprox4d(X, A, B, C, D : Double) : Double;

asm

//add  esp,-$08

//Result := (A*X + B)*(X*X)+(C*X + D);

fld  qword ptr [ebp+$20]

fmul qword ptr [ebp+$28]

fadd qword ptr [ebp+$18]

fld  qword ptr [ebp+$28]

fmul qword ptr [ebp+$28]

fmulp //st(1)

fld  qword ptr [ebp+$10]

fmul qword ptr [ebp+$28]

fadd qword ptr [ebp+$08]

faddp //st(1)

//fstp qword ptr [ebp-$08]

wait

//fld  qword ptr [ebp-$08]

//pop  ecx

//pop  ecx

end;

 

Загружаем X только раз

Code:

function ArcSinApprox4e(X, A, B, C, D : Double) : Double;

asm

//Result := (A*X + B)*(X*X)+(C*X + D);

fld   qword ptr [ebp+$20]

fld   qword ptr [ebp+$28]

//fmul qword ptr [ebp+$28]

fxch

fmul  st(0),st(1)

fadd  qword ptr [ebp+$18]

//fld  qword ptr [ebp+$28]

fld   st(1)

//fmul  qword ptr [ebp+$28]

fmul  st(0),st(2)

fmulp

fld   qword ptr [ebp+$10]

//fmul  qword ptr [ebp+$28]

fmul  st(0),st(2)

fadd  qword ptr [ebp+$08]

faddp

ffree st(1)

wait

end;

Удаляем FXCH и WAIT.

Code:

function ArcSinApprox4f(X, A, B, C, D : Double) : Double;

asm

//Result := (A*X + B)*(X*X)+(C*X + D);

fld   qword ptr [ebp+$28]

fld   qword ptr [ebp+$20]

//fxch

fmul  st(0),st(1)

fadd  qword ptr [ebp+$18]

fld   st(1)

fmul  st(0),st(2)

fmulp

fld   qword ptr [ebp+$10]

fmul  st(0),st(2)

fadd  qword ptr [ebp+$08]

faddp

ffree st(1)

//wait

end;

Переопределяем FFREE ST(1)

Code:

function ArcSinApprox4g(X, A, B, C, D : Double) : Double;

asm

//Result := (A*X + B)*(X*X)+(C*X + D);

fld   qword ptr [ebp+$28]

fld   qword ptr [ebp+$20]

fmul  st(0),st(1)

fadd  qword ptr [ebp+$18]

fld   st(1)

fmul  st(0),st(2)

fmulp

fld   qword ptr [ebp+$10]

fmul  st(0),st(2)

ffree st(2)

fadd  qword ptr [ebp+$08]

faddp

//ffree st(1)

end;

заменяем FMUL/FFREE на FMULP

Code:

function ArcSinApprox4h(X, A, B, C, D : Double) : Double;

asm

//Result := (A*X + B)*(X*X)+(C*X + D);

fld   qword ptr [ebp+$28]

fld   qword ptr [ebp+$20]

fmul  st(0),st(1)

fadd  qword ptr [ebp+$18]

fld   st(1)

fmul  st(0),st(2)

fmulp

fld   qword ptr [ebp+$10]

//fmul  st(0),st(2)

fmulp st(2),st(0)

//ffree st(2)

fadd  qword ptr [ebp+$08]

faddp

end;

Очищаем код и видим, что компилятор еще использует EBP и излишне модифицирует ESP.

Code:

function ArcSinApprox4i(X, A, B, C, D : Double) : Double;

asm

//Result := (A*X + B)*(X*X)+(C*X + D);

fld   qword ptr [ebp+$28]

fld   qword ptr [ebp+$20]

fmul  st(0),st(1)

fadd  qword ptr [ebp+$18]

fld   st(1)

fmul  st(0),st(2)

fmulp

fld   qword ptr [ebp+$10]

fmulp st(2),st(0)

fadd  qword ptr [ebp+$08]

faddp

end;

 

 

Теперь большой вопрос, насколько хорошо эта функция работает.

ArcSinApprox4a        45228

ArcSinApprox4b        45239

ArcSinApprox4c        45228

ArcSinApprox4d        51813

ArcSinApprox4e        49044

ArcSinApprox4f        48674

ArcSinApprox4g        48852

ArcSinApprox4h        44914

ArcSinApprox4i        44914

Мы видим, что в результате «optimizations» на шагах от d до i мы получили «оптимизацию наоборот» на P4, исключая шаг g.

На P3

ArcSinApprox4a        68871

ArcSinApprox4b        68871

ArcSinApprox4c        68634

ArcSinApprox4d        86806

ArcSinApprox4e        85727

ArcSinApprox4f        83542

ArcSinApprox4g        80548

ArcSinApprox4h        88378

ArcSinApprox4i        85324

Мы видим, что оптимизационные шаги d и h очень хороши, а шаги e, f g и I плохие. Вполне возможно, что оптимальной реализации нет. Мы можем выбрать вариант h и удалить оставшиеся и просто сделать несколько вариантов и это путь к быстрой оптимизации.

Так какая же функция победитель? Чтобы найти его мы выберем самую быструю реализацию по каждому решению

На P4

ArcSinApprox1f        47939

ArcSinApprox3g        47416

ArcSinApprox4d        51813

Последняя версия самая быстрая. Параллелизм очень важен на современных процессорах и версия 4 бьет остальных на 9%.

На P3

ArcSinApprox1h        85361

ArcSinApprox3h        87604

ArcSinApprox4h        88378

Версия 4 победитель на P3, но с меньшим преимуществом.

Процессор P4 имеет набор инструкций SSE2, который содержит инструкции для точных расчетов с плавающей запятой. Главная идея этих инструкций The в данном наборе это использование SIMD расчетов. SIMD - это аббревиатура для Single Instruction Multiple Data. «множество данных» (Multiple data) здесь это переменные двойной точности с плавающей запятой (64 bit) и две переменные этих данных могут быть сложены, вычтены, умножены или поделены одной инструкцией. В SSE2 также есть несколько инструкций для скалярных вычислений, которые вычисляют пару этих данных, подобно обычным данным с плавающей запятой на FPU. Наибольшая разница между обычной математикой с плавающей запятой и SSE2 скалярной математикой, в том, что математика с плавающей запятой выполняется на расширенной точности и результат округляется до двойной точности, при копировании в переменную двойной точности в RAM/кэш. Математика SSE2 двойной точности и регистры также двойной точности. Код примеров в данном уроке выполняет несколько вычислений и точность FPU двойная. Если мы загрузим данные, выполним все вычисления и запишем результат, то результат будет только немного меньше, чем при расширенной точности, пока он еще на стеке FPU, и будет округлен до двойной точности, при копировании в переменную. SSE2 вычисления с другой стороны менее точные, в регистре результат также менее точный. При одном вычислении результат будет двойной точности, но когда мы выполним серию вычислений, то накопленная ошибка будет значительно больше. Поскольку FPU выполняет все вычисления с расширенной точностью и хранит промежуточные результаты в регистрах, то можно выполнить много вычислений, прежде чем ошибка станет значимой, ниже двойной точности.

Добавить комментарий

Не использовать не нормативную лексику.

Просьба писать ваши замечания, наблюдения и все остальное,
что поможет улучшить предоставляемую информацию на этом сайте.

ВСЕ КОММЕНТАРИИ МОДЕРИРУЮТСЯ ВРУЧНУЮ, ТАК ЧТО СПАМИТЬ БЕСПОЛЕЗНО!


Защитный код
Обновить