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

 

 

В действительности компилятор также вставил инструкцию MOV и мы можем избавиться от лишних пересылок, но не получим преимущества, поскольку нет удаления мертвого кода. Поэтому производительность остается почти такой же - 43094.

При понимании, где результат записывается в стек, мы сможем оптимизировать строки копирования и перезагрузки их. Результат состоит в том, что здесь уже есть копия переменной Result в стеке. Это уменьшает необходимость извлечения результат из стека FP и загрузки Result из стека. Эта одиночная строка имеет тоже действие, но избыточность удалена.

fst   qword ptr [ebp-$08]

Подобная оптимизация очень часто возможна в коде, сгенерированным компилятором Delphi и об этом важно помнить.

Code:

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

asm

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

fld   qword ptr [esp+$20]

fmul  qword ptr [esp+$28]

fmul  qword ptr [esp+$28]

fmul  qword ptr [esp+$28]

fld   qword ptr [esp+$18]

fmul  qword ptr [esp+$28]

fmul  qword ptr [esp+$28]

faddp

fld   qword ptr [esp+$10]

fmul  qword ptr [esp+$28]

faddp

fadd  qword ptr [esp+$08]

//fstp  qword ptr [esp-$08]

fst  qword ptr [esp-$08]

wait

//fld   qword ptr [esp-$08]

end;

 

 

Данная реализация получила 47939 пункта, и это улучшило результат на 11%.

Следующий вопрос, который мы должны задать себе: А копия Result на стеке используется? Для ответа мы должны проинспектировать код в месте вызова функции.

Y := ArcSinApproxFunction(X, A, B, C, D);

 

call dword ptr [ArcSinApproxFunction]

fstp qword ptr [ebp-$30]

wait

Первая строка после вызова, записывает результат в Y и извлекает из стека. Видя это, мы можем сделать вывод, что результат на стеке не используется, но чтобы быть уверенным мы должны просмотреть также и остаток кода. Если правило для соглашения по регистровому вызову гласит, что результат с плавающей запятой (FP) возвращается в стеке процессора с плавающей запятой, то несколько странно хранить еще и его копию в стеке. Заключаем, что это избыточно копировать Result на стек и затем извлекать его из стека и удали строку, которая делает это.

Code:

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

asm

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

fld   qword ptr [esp+$20]

fmul  qword ptr [esp+$28]

fmul  qword ptr [esp+$28]

fmul  qword ptr [esp+$28]

fld   qword ptr [esp+$18]

fmul  qword ptr [esp+$28]

fmul  qword ptr [esp+$28]

faddp

fld   qword ptr [esp+$10]

fmul  qword ptr [esp+$28]

faddp

fadd  qword ptr [esp+$08]

//fst  qword ptr [esp-$08]

wait

end;

 

Данная функция получила 47405 пункта

Вместо написания всех QWORD PTR [ESP+$XX] строк мы можем писать имена переменных и позволить компилятору рассчитать за нас адреса. Это делает код более безопасным. Если положение переменной будет изменено, то код будет неработоспособным, при использовании жесткой адресации. Это может произойти при смене соглашения по вызову, что конечно бывает редко.

Code:

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

asm

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

//fld   qword ptr [esp+$20]

fld   A

//fmul  qword ptr [esp+$28]

fmul  X

//fmul  qword ptr [esp+$28]

fmul  X

//fmul  qword ptr [esp+$28]

fmul  X

//fld   qword ptr [esp+$18]

fld   B

//fmul  qword ptr [esp+$28]

fmul  X

//fmul  qword ptr [esp+$28]

fmul  X

faddp

//fld   qword ptr [esp+$10]

fld   C

//fmul  qword ptr [esp+$28]

fmul  X

faddp

//fadd  qword ptr [esp+$08]

fadd  D

wait

end;

 

Попробуй оба типа строк

fld   qword ptr [esp+$20]

fld   A

и посмотрите в окне CPU view, что компилятор сгенерировал абсолютно идентичный код для обеих версий.

X используется во многих строках и ссылается не стек. И поэтому загружается со стека во внутренние регистры процессора с плавающей запятой каждый раз. Будет быстрее загрузить X один раз в регистровый стек процессора и изменить все ссылки на него.

Code:

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

asm

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

fld   qword ptr [esp+$20]

fld   qword ptr [esp+$28] //New

fxch

//fmul qword ptr [esp+$28]

fmul  st(0),st(1)

//fmul qword ptr [esp+$28]

fmul  st(0),st(1)

//fmul qword ptr [esp+$28]

fmul  st(0),st(1)

fld   qword ptr [esp+$18]

//fmul qword ptr [esp+$28]

fmul  st(0),st(2)

//fmul qword ptr [esp+$28]

fmul  st(0),st(2)

faddp

fld   qword ptr [esp+$10]

//fmul qword ptr [esp+$28]

fmul  st(0),st(2)

ffree st(2)

faddp

fadd  qword ptr [esp+$08]

fst   qword ptr [esp-$08]

wait

end;

 

 

Добавленная, вторая строка загружает X один раз, для всех операция. Поскольку она загружает X на верхушку стека ST(0), а эта позиция нужна как временная переменная, то мы обменяем регистр ST(0) с ST(1), с помозью инструкции FXCH. Мы также можем поменять местами строки 1 и 2 и получить тот же эффект. Все строки умножения st(0) на X

fmul qword ptr [esp+$28]

мы заменим на

fmul  st(0),st(1)

после последнего использования копии X, мы удалим ее инструкцией FFREE.

Данная реализация получила уже 46882 пункта и ухудшила производительность на 1%. Это стало сюрпризом. Инструкция FXCH объявлена Intel, как не занимающая времени, поскольку используется переименование внутренних регистров. Попробуем проверить это, просто удалив ее.

Code:

function ArcSinApprox1i(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]

//fld   qword ptr [esp+$28]

//fxch

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;

 

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

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

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

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


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