Оптимизация для процессоров семейства Pentium: 20. Цепочки зависимости (PPro, PII и PIII) — Архив WASM.RU

Все статьи

Оптимизация для процессоров семейства Pentium: 20. Цепочки зависимости (PPro, PII и PIII) — Архив WASM.RU

Серии инструкций, где выполнение каждой зависит от результата предыдущей, называется цепочкой зависимости. Большие цепочки нужно по возможности избегать, потому что они делают невозможным выполнение не по порядку и параллельное выполнение.

Пример:

   MOV EAX, [MEM1]
   ADD EAX, [MEM2]
   ADD EAX, [MEM3]
   ADD EAX, [MEM4]
   MOV [MEM5], EAX

В этом примере инструкция ADD генерирует 2 мопа, один для чтения из памяти (порт 2) и один для сложения (порт 0 или 1). Моп чтения может выполняться не по порядку, в то время как моп сложения дожна ждать, пока выполниться предыдущий моп. Эта цепочка зависимости занимает не очень много времени, так как каждое сложение требует только один такт. Но если в вашем коде содержатся медленные инструкции, такие как умножения, или еще хуже - деление, тогда вам определенно нужно сделать что-нибудь, чтобы убрать цепочку зависимости. Это можно сделать, используя разные приемники:

   MOV EAX, [MEM1]         ; начало первой цепочки
   MOV EBX, [MEM2]         ; начало второй цепочки  с другим приемником
   IMUL EAX, [MEM3]
   IMUL EBX, [MEM4]
   IMUL EAX, EBX           ; в конце соединяем цепочки
   MOV [MEM5], EAX

Здесь вторая инструкция IMUL может начаться до того, как будет завершено выполнение первой. Так как у инструкции IMUL вызывает задержку в 4 такта и полностью конвееризована, вы можете использовать до 4-х приемников.

Деление не конвееризовано, поэтому вы не можете делать то же самое со связанными делениями, но, разумеется, вы можете умножить все делители и сделать только одно деление в конце.

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

   FLD [MEM1]         ; начинаем первую цепочку
   FLD [MEM2]         ; начинаем вторую цепочку с другим приемником
   FADD [MEM3]
   FXCH
   FADD [MEM4]
   FXCH
   FADD [MEM5]
   FADD               ; соединяем цепочки в конце
   FSTP [MEM6]

Вам потребуется для этого много инструкций FXCH, но не беспокойтесь: они стоят дешево. Инструкции FXCH обрабатываются в RAT с помощью переименования регистров, поэтому они не создают никакой назгрузки на порты выполнения. Тем не менее, FXCH генерирует один моп в RAT, ROB и в станции вывода из обращения.

Если цепочка зависимости очень длиная, вам может потребоваться три приемника:

        FLD [MEM1]              ; начинаем первую цепочку
        FLD [MEM2]              ; начинаем вторую цепочку
        FLD [MEM3]              ; начинаем третью цепочку
        FADD [MEM4]             ; третья цепочка
        FXCH ST(1)
        FADD [MEM5]             ; вторая цепочка
        FXCH ST(2)
        FADD [MEM6]             ; первая цепочка
        FXCH ST(1)
        FADD [MEM7]             ; третья цепочка

        FXCH ST(2)
        FADD [MEM8]             ; вторая цепочка
        FXCH ST(1)
        FADD                    ; соединяем первую и третью цепочку
        FADD                    ; результат соединяем со второй цепочкой
        FSTP [MEM9]

Избегайте сохранения промежуточных данных в памяти и немедленного их считывания:

        MOV [TEMP], EAX
        MOV EBX, [TEMP]

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

Есть одна ситуация, когда вы не сможете избежать сохранения промежуточных данных в памяти. Она возникает тогда, когда вы перемещаете данные из целочисленного регистра в регистр FPU или наоборот. Например:

        MOV EAX, [MEM1]
        ADD EAX, [MEM2]
        MOV [TEMP], EAX
        FILD [TEMP]

Если вам нечего поместить между записью в TEMP и считыванием из него, вы можете использовать регистр плавающей запятой вместо EAX:

        FILD [MEM1]
        FIADD [MEM2]

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

2002-2013 (c) wasm.ru