Серии инструкций, где выполнение каждой зависит от результата предыдущей, называется цепочкой зависимости. Большие цепочки нужно по возможности избегать, потому что они делают невозможным выполнение не по порядку и параллельное выполнение.
Пример:
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]
Последовательные переходы, вызовы или возвраты также можно считать цепочками зависимости. Производительность этих инструкций равна одному переходу за два такта. Поэтому рекомендуется загружать микропроцессор какой-нибудь полезной работой между переходами.
[C] Агнер Фог, пер. Aquila