Задача диспетчера - подготовить для микрооперации операнды таким образом, чтобы, когда команда прибыла на исполнительное устройство, необходимые для вычисления данные оказались там же. Но конвейер NetBurst устроен так, что диспетчер и собственно исполнительное устройство довольно сильно разнесены по конвейеру, и чтобы данные и микрооперация пришли одновременно, микрооперацию на исполнение требуется запускать задолго до того, как будет получено подтверждение готовности ее операндов. Если быть точным, то после запуска инструкции диспетчером два такта уйдет на подготовку данных, три такта - на исполнение команды и еще один такт - на проверку результатов[В NetBurst, как и в других архитектурах, используется быстрая выборка данных из кэша, когда, грубо говоря, «вначале вытаскиваем данные, а потом уж смотрим, что мы такое вытащили». Выборка происходит при совпадении лишь небольшой части запрошенного и найденного адресов, а проверка на то, что остальная часть адреса тоже совпадает, - производится параллельно с выборкой «вроде как найденных» данных и исполнением операции над ними], после чего инструкции уже можно будет отправлять «в отставку». Стало быть, нужно отправлять инструкцию за пару тактов до того, как данные понадобятся; причем ошиблись мы или нет, станет известно еще позже - тактов эдак через пять-семь, когда диспетчер успеет выпустить соответствующее количество инструкций, часть из которых уже будет выполнена (!). А если мы ошиблись, что тогда делать? Авторы NetBurst предложили весьма своеобразное решение - «реплей».
Снова попробую объяснить ситуацию «на пальцах». Представьте, что конвейер - это рельсовый путь, а по нему бегают вагончики - микрооперации, указывающие, что процессору нужно изготовить и в вагончики погрузить. Причем путей в нашем процессоре несколько, и инструкции разных видов - катятся по разным рельсам. Задача диспетчера, - организовать оптимальным образом движение по своей железнодорожной ветке. Что он делает? Время вагончика в пути от его станции до станции, где вагончик загрузят полезным грузом, ему известно; так что нашему диспетчеру (а всего их семь) остается только связаться с другими диспетчерами и запросить у них информацию о том, когда будут готовы те данные, которые «его» микрооперации используют в качестве исходных, и отправить вагончик с таким расчетом, чтобы он и данные, полученные на другой ветке от другой микрооперации, прибыли на исполнительное устройство одновременно.
Загвоздка в том, что некоторые вагончики имеют обыкновение опаздывать или привозить совсем не то, что запрашивалось, постфактум сообщая о накладке. Соответствующая подобным микрооперациям неспокойная ветка в процессоре отведена для вагончиков, обращающихся к оперативной памяти; причем часто результат выполнения этих микроопераций выступает как исходные данные для другого mОР’а. Поэтому в архитектуре NetBurst порой возникает ситуация, когда микрооперация на исполняющее устройство прибыла, а исходные данные для ее исполнения - нет, Что происходит тогда? Очень простая штука: наш вагончик тут же «переводят на запасной путь», путешествуя по которому, он описывает круг специально рассчитанного размера и возвращается к диспетчеру как раз в тот момент, когда его нужно было бы запускать повторно. Диспетчер приостанавливает отправку на линию новых вагончиков и пропускает вперед прибывший «новый-старый» вагончик. Если к моменту повторного прибытия на исполняющие устройства данных там так и не окажется, микрооперация снова отправится на запасной путь и будет нарезать круги до тех пор, пока нужные данные не появятся. При этом по основному пути могут исполняться другие инструкции, независимые от первой. Получается простое и вроде бы эффективное решение. Хотя стоп! Эффективное ли? Занимавшаяся изучением реплея группа экспертов[Replay: неизвестные особенности функционирования ядра NetBurst .] убедительно показала, что подобная незамысловатая техника приводит к нетривиальным и крайне интересным эффектам вроде «затягивания» в петли реплея целых цепочек данных (вплоть до полного зацикливания - deadlock’а!) и перегрузки исполнительных устройств из-за необходимости многократно исполнять некоторые инструкции. В итоге процессор архитектуры NetBurst «тормозит» даже при отсутствии ошибок предсказания - просто в силу того, что несвоевременно запущенные цепочки инструкций приходится переисполнять целиком, вместо того чтобы переисполнить одну-единственную «неудачную» инструкцию. Вдобавок, поскольку исполнительные устройства греются больше всех остальных узлов процессора, то непрерывная прогонка через них потока инструкций, данные для которого не подготовлены, приводит еще и к тому, что процессор не просто «тормозит», а вовсю греется. Не очень приятные эффекты, но это та цена, которую уплатила Intel за разработку процессора, умеющего работать на очень высоких тактовых частотах. И если учесть успех процессоров на ядре Northwood - цена вполне оправданная. К сожалению, непомерное тепловыделение NetBurst-процессоров начиная с некоторого момента замедлило, а потом и вовсе остановило рост тактовой частоты, так что сегодня минусы скорее перевешивают плюсы этой, несомненно, опередившей свое время архитектуры.
Просуммируем все сказанное. Теоретически процессор архитектуры NetBurst способен обрабатывать четыре инструкции за такт (два «быстрых» ALU, работающих на удвоенной частоте). При тактовой частоте от 2,53 до 3,8 ГГц столь высокий показатель должен был бы вывести NetBurst-процессоры в лидеры по производительности, если бы не недостаточно быстрый Front-end, неспособный обеспечить больше трех микроопераций за такт; если бы не крайне ограниченный набор «быстрых» инструкций, в которых вплоть до ядра Prescott не входила, например, широко используемая простая операция битового сдвига[Кстати, даже в Prescott битовый сдвиг поддерживает только одно Fast ALU из двух. Это и ряд других ограничений связаны с оригинальной организацией 32-битного Fast ALU в виде двух «сдвоенных» 16-битных ALU]; если бы не наличие всего лишь одного (!) блока ALU и одного блока FPU, умеющих работать со «всей остальной» арифметикой (причем целочисленное умножение вплоть до того же Prescott, тоже выполнялось в FPU!); если бы не многочисленные штрафные такты, возникающие, например, при обращении к «невыровненным» данным в оперативной памяти; если бы не система реплея… если бы не десятки разных «если», подрезающих этой архитектуре крылья.