Шаг второй. RISC
   Точно так же, как когда-то CISC-процессоры проектировались под нужды asm-программистов, RISC проектировался в расчете на типовой код, генерируемый компиляторами. Для начала разработчики свели к минимуму набор инструкций и к абсолютному минимуму — количество режимов адресации памяти; упаковав все, что осталось, в простой и удобный для декодирования регулярный машинный код. В частности, в классическом варианте RISC из инструкций, обращающихся к оперативной памяти, оставлены только две (Load — загрузить данные в регистр и Store — сохранить данные из регистра; так называемая Load/Store-архитектура), и нет ни одной инструкции вроде вычисления синуса, косинуса или квадратного корня (их можно реализовать «вручную»), не говоря уже о более сложных[Канонический пример — инструкция INDEX, выполнявшаяся на VAX медленнее, чем вручную написанный цикл, выполняющий ровно тот же объем работы]. Да что там синус с косинусом — в некоторых RISC-процессорах пытались отказаться даже от трудно реализуемого аппаратного умножения и деления! Правда, до таких крайностей ни один коммерческий RISC, к счастью, не дошел, но как минимум две попытки (ранние варианты MIPS и SPARC) предприняты были.
   Второе важное усовершенствование RISC-процессоров, целиком вытекающее из Load/Store-архитектуры, — увеличение числа GPR (регистров общего назначения). Варианты, у которых меньше шестнадцати GPR, — большая редкость, причем почти все эти регистры полностью равноправны, что позволяет компилятору свободно распоряжаться ими, сохраняя большую часть промежуточных данных именно там, а не в стеке или оперативной памяти. В некоторых архитектурах, типа SPARC, «регистровость» возведена в абсолют, в некоторых — оставлена на разумном уровне; однако почти любой RISC-процессор обладает куда большим набором регистров, чем даже самый продвинутый CISC. Для сравнения: в классическом x86 IA-32 всего восемь регистров общего назначения, причем каждому из них приписано то или иное «специальное назначение» (скажем, в ESP хранится указатель на стек) затрудняющее или делающее невозможным его использование.
   Среди прочих усовершенствований, внесенных в RISC, — такие нетривиальные идеи, как условные инструкции ARM или режимы работы команд[Например, некий модификатор в архитектуре PowerPC и некоторых других показывает, должна ли инструкция выставлять по результатам своего выполнения определенные флаги, которые потом может использовать инструкция условного перехода, или не должна. Это позволяет разнести в пространстве инструкцию, выполняющую вычисление условия, и инструкцию собственно условного перехода — что в конвейерных архитектурах зачастую позволяет процессору не «гадать», будет совершен переход или нет, а сразу достоверно это знать]. В классическом CISC они почти не встречаются, поскольку на момент разработки соответствующих наборов инструкций ценность этих решений была сомнительной (они выйдут на сцену только в конвейеризированных процессорах).
   «В чистом виде» идею «легкого» RISC-процессора можно встретить у компании ARM с ее невероятно простыми и тем не менее весьма эффективными 32-разрядными CPU. Но простота далеко не главный показатель в процессоре, и как самоцель подход RISC в целом себя, наверное, не оправдал бы — резко уменьшившаяся сложность CPU и сопутствующее увеличение тактовой частоты и ускорение исполнения инструкций хорошо уравновешивались возросшими размерами программ и сильно упавшей их вычислительной плотностью[Среднее количество вычислений на единицу длины машинного кода]. К счастью, в то же время, когда начались разработки первых коммерческих RISC-процессоров, был сделан…
 
   Условные инструкции ARM
   Архитектура ARM (Advanced RISC Machines) разработана в 1983—85 годах в компании Acorn Computers. Команда Роджера Вильсона и Стива Фербера взяла за основу набора инструкций ARM некогда популярный, а ныне забытый процессор MOS Technology 6502 и снабдила его специальным четырехбитным кодом условия, которым можно было дополнить любую инструкцию.
   Идея условных инструкций проста, как все гениальное: инструкция с условным кодом выполняется, только если в процессоре выставлен бит соответствующего условия. В противном случае она игнорируется. Ближайший аналог в наборе инструкций x86 — инструкции условного перехода, срабатывающие, только если в процессоре был выставлен тот или иной флаг; в архитектуре ARM подобные «условности» применимы к любой инструкции, а флаги можно определять самостоятельно. Идея в том, что в коде типа
   Если (условие) то Выполнить1 иначе Выполнить 2
   вместо того, чтобы записать традиционную конструкцию
   1 Вычислить условие
   2 Если условие выполнено, то идти к 5
   3 Выполнить2
   4 Идти к 6
   5 Выполнить1
   используя условные инструкции, можно записать
   1 Вычислить условие и поставить Флаг1 по результатам вычисления
   2 Выполнить1 при условии выставленного Флаг1
   3 Выполнить2 при условии невыставленного Флаг1
   Обратите внимание, что получившийся код не только более компактен, но и лишен одного условного и одного безусловного перехода, присутствовавших в классическом варианте, — тех самых переходов, которые обычно больно бьют по производительности конвейерных архитектур.
   Еще ряд дополнений в ARM предусматривал введение инструкций, одновременно выполняющих несколько простых операций, тем самым избавляя регистры процессора от необходимости сохранять результаты промежуточных вычислений и увеличивая вычислительную плотность кода. Этот подход нетипичен для RISC-процессоров, поскольку плохо вписывается в «основную идею» их максимального упрощения, но в конечном счете он привел к тому, что процессоры Acorn при прочих равных получили большую производительность на единицу частоты. Конечно, ARM-подход тоже имеет недостатки (например, необходимость выполнять пустые инструкции), однако в общем и целом он позволяет создавать очень простые процессоры с очень хорошей производительностью.
   В 1985 вышел первенец архитектуры ARM — 32-разрядный процессор ARM1; в 1986-м — первый коммерческий вариант архитектуры, процессор ARM2. ARM2 был настоящим шедевром — в его ядре насчитывалось всего 30 тысяч транзисторов (вчетверо меньше, чем в i80286, и втрое меньше, чем в MC68000); он отличался крайне низким энергопотреблением и обладал при всем при том производительностью, превосходящей производительность 286-го процессора (не говоря уже о том, что 286-й был 16-разрядным, а ARM2 — 32-разрядным процессором). Немного позже увидел свет и ARM3, в котором появилось четыре килобайта кэш-памяти, еще увеличившей производительность процессоров ARM.
   Трудно сказать, ожидала ли Acorn Computers такого успеха, однако воспользовалась им в полной мере. В 1990 году Acorn, работавшая над развитием ARM уже в сотрудничестве с Apple, преобразовала подразделение, занимавшееся ARM, в отдельную фирму— Advanced RISC Machines. Результатом совместной работы стало ядро ARM6 и процессор ARM610, использовавшийся, в частности, в одном из первых КПК в мире — Apple Newton. Ядро ARM6 было по-прежнему невероятно простым (всего 35 тысяч транзисторов!), мало потребляющим и обеспечивало приличный уровень производительности. Поскольку тягаться в производительности с гораздо более сложными монстрами вроде i386 оно не могло (да и ниша высокопроизводительных вычислений была прочно занята MIPS), руководство Advanced RISC Machines избрало оригинальный способ ведения бизнеса — позиционировав ARM6 как «встраиваемое» вычислительное ядро, которое любой желающий за сравнительно небольшие деньги мог интегрировать в свои специализированные процессоры. Ядро ARM6 вышло столь удачным и так хорошо подходило для этой бизнес-модели (благодаря простоте, его можно было изготавливать даже на сильно устаревшем дешевом оборудовании), что вскоре архитектура ARM получила широчайшее распространение. Самый яркий пример подобного «гибрида» — ядро ARM7TDMI, являющееся основой для подавляющего большинства процессоров сотовых телефонов. Сегодня ARM используется в более чем 75% всех интегрированных процессоров, выпускаемых в мире, — от контроллеров жестких дисков, калькуляторов и микропроцессоров игрушек до сетевых маршрутизаторов. То есть там, где от процессора не требуется очень высокого быстродействия.
   Другое направление, которым сегодня «жива» ARM, — это более производительная архитектура StrongARM, широко используемая в КПК, коммуникаторах и некоторых терминалах. StrongARM была разработана в 1995 году компанией DEC совместно с ARM; а позднее, после судебного разбирательства, — продана вместе с соответствующим подразделением корпорации Intel, которая сейчас и занимается ее развитием в виде линейки процессоров XScale.
   Шаг 3. Введение конвейера
   Идея конвейера, давным-давно предложенная Генри Фордом, состоит в том, что производительность цепочки последовательных действий определяется не сложностью этой цепочки, а лишь длительностью самой сложной операции. Иными словами, совершенно неважно, сколько человек занимаются производством автомобиля и как долго длится его изготовление в целом, — важно то, что если каждый человек в цепочке тратит, скажем, на свою операцию одну минуту, то с конвейера будет сходить один автомобиль в минуту, ни больше и ни меньше; независимо от того, сколько операций нужно совершить с отдельным автомобилем и сколько заняла бы его сборка одним человеком. Применительно к процессорам принцип конвейера означает, что если мы сумеем разбить выполнение машинной инструкции на несколько этапов, то тактовая частота (а вернее, скорость, с которой процессор забирает данные на исполнение и выдает результаты) будет обратно пропорциональна времени выполнения самого медленного этапа. Если это время удастся сделать достаточно малым (а чем больше этапов на конвейере, тем они короче), то мы сумеем резко повысить тактовую частоту, а значит, и производительность процессора.
   Процедуру выполнения практически любой инструкции можно разбить как минимум на пять непересекающихся этапов:
   Выборка инструкции (FETCH) из памяти. Из программы извлекается инструкция, которую нужно выполнить.
   Декодирование инструкции (DECODE). Процессор «соображает», что от него хотят, и переправляет запрос на нужное исполнительное устройство.
   Подготовка исходных данных для выполнения инструкции.
   Собственно выполнение инструкции (EXECUTE).
   Сохранение полученных результатов.
   Конвейеризация потенциально применима к любой процессорной архитектуре, независимо от набора команд и положенных в ее основу принципов. Даже самый первый x86-процессор, Intel 8086, уже содержал своеобразный примитивный «двухстадийный конвейер» — выборка новых инструкций (FETCH) и их исполнение осуществлялись в нем независимо друг от друга. Однако реализовать что-то более сложное для CISC-процессоров оказалось трудно: декодирование неоднородных CISC-инструкций и их очень сильно различающаяся сложность привели к тому, что конвейер получается чересчур замысловатым, катастрофически усложняя процессор. Подобных трудностей у RISC-архитектуры гораздо меньше (а SPARC и MIPS, например, и вовсе были специально оптимизированы для конвейеризации), так что конвейеризированные RISC-процессоры появились на рынке много раньше, чем аналогичные x86.
   Недостатки конвейера неочевидны, но, как обычно и бывает, из-за нескольких «мелочей» реализовать грамотно организованный конвейер совсем не просто. Основных проблем три.
   Необходимость наличия блокировок конвейера. Дело в том, что время исполнения большинства инструкций может очень сильно варьироваться. Скажем, умножение (и тем более деление) чисел требуют (на стадии EXECUTE) нескольких тактов, а сложение или побитовые операции — одного такта; а для операций Load и Store, которые могут обращаться к разным уровням кэш-памяти или к оперативной памяти, это время вообще не определено (и может достигать сотен тактов). Соответственно, должен быть какой-то механизм, который бы «притормаживал» выборку и декодирование новых инструкций до тех пор, пока не будут завершены старые. Методов решения этой проблемы много, но их развитие приводит к одному — в процессорах прямо перед исполнительными устройствами появляются специальные блоки-диспетчеры (dispatcher), которые накапливают подготовленные к исполнению инструкции, отслеживают выполнение ранее запущенных инструкций и по мере освобождения исполнительных устройств отправляют на них новые инструкции. Даже если исполнение займет много тактов — внутренняя очередь диспетчера позволит в большинстве случаев не останавливать подготавливающий все новые и новые инструкции конвейер[Новые инструкции тоже не каждый такт удается декодировать, так что возможна и обратная ситуация: новых инструкций за такт не появилось, и диспетчер отправляет инструкции на выполнение «из старых запасов»]. Так в процессоре возникает разделение на две независимо работающие подсистемы: Front-end (блоки, занимающиеся декодированием инструкций и их подготовкой к исполнению) и Back-end (блоки, собственно исполняющие инструкции).
   Необходимость наличия системы сброса процессора. Поскольку операции FETCH и EXECUTE всегда выделены в отдельные стадии конвейера, то в тех случаях, когда в программном коде происходит разветвление (условный переход), зачастую оказывается, что по какой из веток пойти — пока неизвестно: инструкция, вычисляющая код условия, еще не выполнена[Вот здесь-то и нужны те самые режимы выставления флагов PowerPC, о которых шла речь в сноске 2]. В результате процессор вынужден либо приостанавливать выборку новых инструкций до тех пор, пока не будет вычислен код условия (а это может занять очень много времени и в типичном цикле страшно затормозит процессор), либо, руководствуясь соображениями блока предсказания переходов, «угадывать», какой из переходов скорее всего окажется правильным.
   Наконец, конвейер обычно требует наличия специального планировщика (scheduler), призванного решать конфликты по данным. Если в программе идет зависимая цепочка инструкций (когда инструкция-2, следующая за инструкцией-1, использует для своих вычислений данные, только что вычисленные инструкцией-1), а время исполнения одной инструкции (от момента запуска на стадию EXECUTE и до записи полученных результатов в регистры) превосходит один такт, то мы вынуждены придержать выполнение очередной инструкции до тех пор, пока не будет полностью выполнена ее предшественница. К примеру, если мы вычисляем выражение вида A•B+C с сохранением результата в переменной X (XfA•B+C), то процессор, выполняя соответствующую выражению цепочку из двух команд типа R4fR1•R2; R0fR3+R4, должен вначале дождаться, пока первая инструкция сохранит результат умножения A•B, и только потом прибавлять к полученному результату число С. Цепочки зависимых инструкций в программах — скорее правило, нежели исключение, а исполнение команды с записью результата в регистры за один такт — наоборот, скорее исключение, нежели правило, поэтому в той или иной степени с проблемой зависимости по данным любая конвейерная архитектура обязательно сталкивается. Оттого-то в конвейере и появляются сложные декодеры, заранее выявляющие эти зависимости, и планировщики, которые запускают инструкции на исполнение, выдерживая паузу между запуском главной инструкции и зависимой от нее.
   Ну как вам список проблем? Идея конвейера в процессоре очень красива на словах и в теории, однако реализовать ее даже в простом варианте чрезвычайно трудно. Но выгода от конвейеризации столь велика и несомненна, что приходится с этими трудностями мириться, ведь ничего лучшего до сих пор не придумано.
   В 1991—92 годах корпорация Intel, освоив производство сложнейших кристаллов с более чем миллионом транзисторов, выпустила i486 — классический CISC-процессор архитектуры x86, но с пятистадийным конвейером. Чтобы вы смогли оценить этот рывок, приведу две цифры: тактовую частоту по сравнению с i386 введение конвейера позволило увеличить втрое, а производительность на единицу частоты — вдвое[В i386 многие инструкции выполнялись за несколько тактов; а в i486 среднее «время» исполнения инструкции в тактах удалось снизить почти вдвое]. Правда, расплатой за это стала чудовищная сложность ядра i486; но такие «мелочи» по меркам индустрии центральных процессоров — пустяк: быстро растущие технологические возможности кремниевой технологии уже через пару лет позволили освоить производство i486 всем желающим. Но к тому моменту RISC-архитектуры сделали еще один шаг вперед — к суперскалярным процессорам
 
   MIPS-архитектура: «Pentium 4» 80-х годов
   MIPS (Microprocessor without Interlocked Pipeline Stages), «процессор без блокировок в конвейере». Основная идея, которой руководствовался Джон Хеннеси, со своей командой проектировавший в 1981 году первый MIPS-процессор, такова. Сильно упростив внутреннее устройство CPU и используя очень длинный (по тем временам) конвейер, можно получить процессор, не умеющий выполнять сравнительно сложные инструкции, зато работающий на очень высоких тактовых частотах, позволяющих скомпенсировать потери производительности на эмуляцию этих сложных инструкций. Изначально предполагалось, что MIPS-процессоры не будут аппаратно поддерживать даже операции умножения и деления — благодаря чему можно было обойтись без сложных в реализации блокировок конвейера[Процедура приостановки конвейера, инициируемая, когда процессору встречается «медленно выполняющаяся» операция, которую невозможно выполнить на какой-то из стадий за один такт. В процессорах тех времен такими операциями являлись умножение и деление; в современных процессорах блокировку может вызвать неудачное обращение в оперативную память, не находящуюся в кэше CPU] (отсюда и название архитектуры). Тем не менее даже в самых первых MIPS’ах блокировки в конвейере, равно как и аппаратные инструкции умножения и деления все-таки присутствовали — «в чистом виде» идея оказалась малопригодной для создания коммерческих процессоров.
   В 1984 году Хеннеси с командой покинул Стэндфордский университет и основал компанию MIPS Computer Systems. В 1985 она выпустила первый 32-разрядный MIPS-процессор R2000; в 1988 году — гораздо более быстрый, работающий с виртуальной памятью и поддерживающий многопроцессорность R3000. R3000 стал первым по-настоящему коммерчески успешным MIPS-процессором и использовался в рабочих станциях Silicon Graphics. Кстати, вариант MIPS R3000A хорошо известен в народе как центральный процессор приставки Sony PlayStation
   В 1991 году вышел первый 64-разрядный MIPS R4000, легший в основу целого ряда различных процессоров, выпускавшихся по лицензиям другими фирмами. R4000 оказался настолько важен для SGI, что она не колеблясь приобрела испытывавшую тогда финансовые затруднения MIPS Computer Systems и превратила эту компанию в собственное подразделение MIPS Technologies. Тогда же SGI начала продавать лицензии на производство MIPS-процессоров сторонним фирмам, которые взялись разрабатывать свои, улучшенные варианты R4000. Помимо всего прочего, начиная с R4600 и R4700 (разработка Quantum Effects Devices) MIPS-процессоры стали основой для знаменитых маршрутизаторов Cisco, являющихся сегодня неотъемлемой частью большинства крупных сетей, включая Интернет. Использовались 64-разрядные MIPS-процессоры и в приставках: R4300 — в Nintendo 64, R5900 — в PlayStation 2.
   В 1994 году вышел R8000 — первый суперскалярный MIPS-процессор; в 1995-м — R10000, улучшенный во всех отношениях вариант R8000, поддерживавший внеочередное исполнение команд в конвейере. Работая на частоте 200 МГц, R10000 был одним из самых быстрых CPU того времени. Пожалуй, на те времена пришелся расцвет архитектуры MIPS — она была столь успешной, что в 1998 году SGI снова сделала из MIPS Technologies отдельную компанию. Правда, «в стиле Тараса Бульбы» («я тебя породил, я тебя и убью»), — SGI сочла дальнейшее развитие MIPS как своей флагманской разработки бесперспективным и решила, когда настанет срок, перевести линейку Silicon Graphics на процессоры архитектуры IA-64 (Intel Itanium).
   В итоге дизайн всех последующих MIPS-процессоров основывался на R10000. Изменялись только объем кэш-памяти и постепенно наращивалась тактовая частота. Фактически после прорыва R10000 архитектура MIPS была заброшена[После очередного сообщения о задержке выпуска Itanium, выходила очередная версия MIPS R1xxxx. Причем этих задержек было столько, что MIPS Technologies помаленьку добралась и до R16000A с тактовой частотой 800 МГц], и мало-помалу эти процессоры утратили лидирующее положение в индустрии. В 2001 году топовым CPU от MIPS Technologies был R14000 с тем же старым ядром R10000 и тактовой частотой всего 600 МГц. Конкуренты в лице, к примеру, более совершенных в технологическом плане AMD Athlon уже достигли частот 1,3—1,4 ГГц, были в несколько раз производительнее, а стоили куда меньше. Так что как «тяжелая высокопроизводительная RISC-архитектура» MIPS к началу нового тысячелетия умерла. Но компания MIPS Technologies процветает до сих пор — за счет лицензирования архитектуры сторонним разработчикам.
   Еще в 1999 году MIPS Technologies упростила свою лицензионную политику, предложив всем желающим два варианта MIPS-архитектуры: MIPS32 — для 32-разрядных систем и MIPS64 — для 64-разрядных. С тех пор эту технологию лицензировали NEC, Toshiba, Broadcom, Philips, LSI Logic и IDT, выпустившие огромное количество специализированных интегрированных процессоров на ее основе. Сегодня MIPS — самая популярная высокопроизводительная[То есть там, где производительность критична, используется MIPS, а где нет — ARM] архитектура, использующаяся во встраиваемых системах. А это львиная доля сетевых устройств (от роутеров Cisco Systems до небольших мостов домашних и офисных сетей); большая часть процессоров игровых приставок прошлых поколений; процессоры для WiFi и VoIP; кодеры-декодеры MPEG; некоторая часть процессоров терминалов, КПК и сотовых телефонов. Не очень завидная участь для бывшего лидера, но если сравнивать с судьбой SPARC или Alpha — не такая уж и плохая.
   Шаг 4. Суперскалярные и Out-of-Order-процессоры
   У полноценной конвейеризации, более или менее эффективно обходящей перечисленные выше проблемы, есть одно несомненное достоинство: она настолько сложна, что, единожды реализованная, позволяет легко построить на ее основе целый ряд интересных новшеств. Для начала заметим, что коль уж у нас есть очереди готовых к исполнению инструкций и мы знаем взаимозависимости между ними по данным, есть техника переименования регистров, позволяющая разным инструкциям одновременно задействовать одни и те же регистры для разных целей, и, наконец, есть надежно работающая система сброса конвейера, то мы можем:
   Запускать на исполняющие устройства сразу несколько инструкций (если они не зависят друг от друга и могут быть безболезненно выполнены одновременно).
   Переупорядочивать независящие друг от друга инструкции так, как сочтем нужным.
   Процессоры, использующие первую технику, называются суперскалярными. К примеру, сугубо теоретически, по числу исполнительных устройств, Pentium 4 может выполнять семь инструкций за такт, а Athlon 64 — девять. Реальные цифры, конечно, гораздо скромнее и определяются трудностью полноценной загрузки всех исполнительных устройств, однако Pentium 4 все же способен исполнять в устоявшемся режиме две (при некоторых условиях — четыре), а Athlon 64 — три инструкции за такт, одновременно производя две (A64 — три) операции по адресации и выборке данных из оперативной памяти. Может показаться, что реализация суперскалярного процессора очень проста (достаточно со стадии schedule просто распределять инструкции по разным исполнительным устройствам), однако такой лобовой подход обычно упирается в то, что Front-end процессора перестает успевать загружать исполнительные блоки работой. Поэтому на практике хорошо сделанные суперскалярные архитектуры, подобные AMD K7/K8, приходится специально «затачивать» под суперскалярность.
   Процессоры, использующие вторую технику, называются процессорами с внеочередным исполнением инструкций (Out-of-Order processors, OoO). Техника переупорядочивания инструкций замечательна тем, что резко ослабляет негативные эффекты от медленной оперативной памяти и от наличия зависимых цепочек инструкций. Если, например, инструкция A обратилась к оперативной памяти, а нужных данных в кэше не оказалось или если A занимается ожиданием результатов выполнения какой-то другой инструкции, то OoO-процессор сможет пропустить вперед другие инструкции, не зависящие от результатов выполнения инструкции A. Кроме того, продвинутый планировщик OoO-процессора иногда может использоваться для реализации специфических деталей той или иной архитектуры — например, для спекулятивного исполнения по данным в случае Pentium 4 или одновременного исполнения нескольких веток программного кода в IA-64. Реализация OoO-процессоров не требует специальной оптимизации всего конвейера — это всего лишь усложнение схемы планировщиков, запускающих готовые к исполнению инструкции на исполнительные устройства в другом порядке, нежели они на планировщики поступили, плюс усложнение схем сброса конвейера и сохранения полученных результатов: результат выполнения прошедших вне очереди инструкций все равно должен сохраняться в последовательности, строго соответствующей расположению инструкций в изначальном коде[Это связано с тем, что если случится какая-то ошибка, то результаты выполнения запущенных вперед очереди инструкции придется аннулировать].
   На сегодняшний день не существует ни одного суперскалярного или OoO CISC-процессора. Дело в том, что поскольку для нормальной реализации навороченных диспетчеров и планировщиков все равно требуется длительная и тщательная подготовка инструкций, причем желательно — до такого простого состояния, чтобы эти функционирующие на огромных частотах модули особенно не «задумывались» над тем, что такая хитрая последовательность байтов означает и куда ее следует направить (проблем у них и без того хватает), то любой исходный машинный код Front-end процессоров превращает перед исполнением в некое внутреннее, упрощенное и «разжеванное», состояние. То есть на этом этапе развития различия между RISC— и CISC-архитектурами почти стираются — просто у RISC’ов декодер, превращающий исходный машинный код в содержимое очередей планировщиков, устроен гораздо проще, чем «расковыривающий» хитро упакованные x86-инструкции CISC-подобный декодер AMD Athlon и Intel Pentium. Так что можно сказать, что фактически все современные x86-процессоры «в глубине души» являются полноценными RISC’ами — ведь исходный x86-код они в любом случае преобразуют на лету во внутреннее RISC-подобное представление. Правда, разной сложностью декодеров дело не ограничивается: все-таки классический RISC-код не только проще преобразовывать, но и результирующий внутренний код из него получается лучше — планировщикам гораздо легче его обрабатывать (в нем меньше зависимостей и операций с оперативной памятью). Вот и появляются в x86 все новые и новые расширенные наборы инструкций (от 3Dnow! до SSE): это всего-навсего «внешняя ширма», упрощающая работу декодерам инструкций и позволяющая им сгенерировать более эффективный внутренний код. Специального блока обработки того же упакованного 128-битного формата SSE нет ни в одном современном процессоре, так что когда в программном коде x86 встречается, скажем, инструкция сложения двух регистров SSE по четыре числа в каждом — декодер банально генерирует код из четырех явно независимых (вот за что боролись!) инструкций сложения, которые планировщику потом будет легко разбросать по исполнительным устройствам. Но какого-либо «специального блока SSE», одновременно выполняющего запрошенные одной инструкцией четыре сложения, ни в Athlon, ни в Pentium 4 нет.