Pull to refresh

Comments 171

Я насчитал 945 в Ice Lake; другой считавший в 2016 насчитал 981 без уточнения, что именно и где именно он считал.

Это скорее всего не учитывает AVX-512, там добавилось еще солидно инструкций (хотя раз считали Iceв Lkae то должны были учесть)

Комментатор имел в виду — как хорошо что есть конкуренция, но был не понят.

Но причём здесь AMD и конкуренция? Intel ведь не упомянут в статье.

Ось X на графике состоит только из процессоров Intel.
И только благодаря наличию конкурента — в лице AMD происходило добавление новых инструкций.

Хм, также в тэгах тоже только их процы. Но неужели если бы не AMD, то Intel не ускоряла бы декодирование и не добавляла шифрование?

Объяснили бы хоть для чего нужны различные инструкции, какие процессы они ускоряют, и тп.
А то количество инструкций и загуглить можно, но в чем польза то? Все равно, что написать «в стране X население Y», ок, рад что получил эту бесполезную информацию, которая находится в интернете за 1 секунду…
Если бы вы попытались загуглить «количество инструкций в x86», то вы бы увидели, что готового ответа нет ни у кого.

Есть неплохая книга "Modern X86 Assembly Language Programming: 32-bit, 64-bit, SSE, and AVX".

Я бы ещё порекомендовал бы Agner fog. Там всё детально разбирается. Ну и официальные гайды, конечно же.

Это же не веб). Тут технологии не стареют десятилетиями. Некоторые вещи по Windows 3.1 актуальны до сих пор (с небольшими доработками, но все же).
Книга вполне хорошая, и объясняет основные моменты, а если нужно самое свежее и самое полное руководство — есть мануалы Интела.

Любопытно какой процент транзисторов пожертвован на обратную совместимость.
А вообще, это грустно. Я пытался как-то сделать дизассемблер x86, но вовремя остановился. Потом оценил объём сопутствующего кода, который присутствует в известных дизассемблерах, вроде CapStone.

«Объём сопутствующего кода» в основном зависит не от сложности дизассемблирования, а от сложности порождения чего-то архитектурно-независимого на выходе.

Просто дизассемблер x86 я делал когда AVX и AVX2 уже были, а AVX512 ещё не было. Заняло пару недель, сравнение с XEDом и Objdump'ом показало, что текстовый выхлоп совпадает на всех инструкциях.

А вот если вы хотите сделать что-то, что работает и с x86 и с ARM и со всем существующим зоопарком… вот тут да — задачка будет посложнее.

Две недели — это, на мой взгляд, быстро. Много кофе выпили?
В моём случае поддержка x86 была необязательной опцией и сводилась по большей части к дизассемблеру длин.
Поделка анализировала предельную глубину используемого стека в программах под ARM (если встречала alloca, либо рекурсию — ругалась). Начиная с точки входа потока выводилось дерево вызовов со счётчиком накопленного стекового расхода в каждом узле. Незаменимый инструмент в ряде случаев.

Две недели — это, на мой взгляд, быстро. Много кофе выпили?
Там не нужно «много кофе», на самом деле. Нужно примерно день на чтение мануала и медитацию.

В x86 есть примерно дюжина «странных» инструкций из «ранней истрии вопроса» (вот всякие эти BOUND/ENTER/MOVS/...).

Но подавляющее большинство инструкций следуют строгому шаблону (описан тут, а тут есть прямо даже красивая картинка). Пара дней уходит на декодер «абстрактной x86-инструкции», неделька на то, чтобы перенести все эти тысячи инструкций в тот формат описания, что вы себе придумали (может быть это можно как-то ускорить и вытащить данные из PDF… я не стал заморачиваться), ну и ешё день-два — на борьбу вот с теми «извращениями», которые из раннего х86 пришли к нам (все современные инструкции начиная с MMX и новее имеют регулярную форму, только EXTRQ из SSE4A выделяется).

Мне тоже не нужен был «полноценный декодер», но зато была очень важна его скорость.

На самом деле самое «весёлое» это не вот все эти тысячи инструкций, но подобного рода косячки.

Иструкции из тех, чтоб были уже в самом-самом первом 8086, но по разному декодирующиеся в AMD64 и Intel 64… это ж просто праздник какой-то!

Спасибо за полезную информацию. Вы вселили в меня осторожный оптимизм касательно дизассемблирования x86.

Ну тут нужно подумать вот о чём. Декодеров в современном x86 CPU не много, а… очень много. Штук двадцать. Спршивается: куда столько? А вот… супер-скаляр-с. Вам нужно уметь декодировать 4 инструкции за такт… но как это сделать, если у них и длина-то неизвестно какая? Что именно мы декодировать-то собрались?

Применяется простой приём: есть куча (16 обычно) «простых» декодеров (которые могут узнать только длину инстукции) и ещё 3-4 «сложных» — которые уже могут декодировать инстукции до конца.

Потому декодирование стремятся сделать как можно более простым. Транзисторный бюжет ведь ограничен! Потому, в частности, AMD «выкинула на помойку» 3DNow! и SSE4a в 2012м (в «серии тяжёлая техника» — где появилась как раз поддержка AVX) и TBM и XOP в 2017м (Ryzen). Не потому что их сложно реализовать. А потому что их сложно декодировать!

Так что… с течением времени, как ни странно, декодирование сильно сложнее не становится — инструкции всё более регулярные, наоборот. А вот «унаследованные инструкции»… вот там да — содом и гоморра.
Потому, в частности, AMD «выкинула на помойку» 3DNow! и SSE4a в 2012м (в «серии тяжёлая техника» — где появилась как раз поддержка AVX) и TBM и XOP в 2017м (Ryzen). Не потому что их сложно реализовать. А потому что их сложно декодировать!

а как же обратная совместимость?

Кое-где потрачена, видимо, решив, что не сильно нужно. Например, я столкнулся с багами рендера в игре Mass Effect на AMD FX.

Это другое. Некоторые инструкции просто дают разный результат на AMD, Intel и VIA. В частности пресловутый Reciprocals of Square Roots.

Они дают ошибку не более описанной, да, но вот побитовые значения — разные.

А если игра на это заложилась…

Самая большая беда — для многопользовательских игр: там не так важно, какой именно результат получается — важно, чтобы у всех игроков один и тот же. А тут, вдруг, такой «подарочек»…

Ну, в этом смысле амд действительно правы — зачем вам 3Dnow!, если есть AVX. Пользуйтесь лучшими технологиями ))) К тому же 3Dnow! был разный — это не одно расширение, а чуть ли не три или четыре. С разными дополнительными наборами команд

К тому же 3Dnow! был разный — это не одно расширение, а чуть ли не три или четыре.
Два с половиной. 3DNow!, Extended 3DNow! и ещё пара специнструкций у Geode.

Последнее связано с тем, что Geode — это вообще разработка другой фирмы (Cyrix, который National Semiconductor купила, а потом перепродала AMD).
Ещё в Pentium появилась инструкция CPUID (и в старшие модели 486х потом перекочевала).

Если программа не проверяет CPUID — то ССЗБ, это бага, чинить нужно.
UFO just landed and posted this here
UFO just landed and posted this here
А более старые версии — это какие? У нас в проекте это начали использовать только после перехода на clang 10, но я сейчас проверил: clang 7 тоже уже так умеет…
UFO just landed and posted this here
У AMD нет такой проблемы как «обратная совсестимость». С их долей рынка все равно мало кто пользуется AMD-only инструкциями потому что не могут себе позволить чтобы код работал только на AMD.

В итоге Capstone-то использовали?
Интересно, что последняя версия вышла в начале этого месяца, а баг с декодированием mulsd в ней тоже присутствует. Но разработчик написал, что в пятой версии уже исправлено. Вот насколько x86 сложна, бедные разработчики Capstone...

Успешно поэкспериментировал с Capstone и ещё с некоторыми дизассемблерами, но потом ограничился своими силами и поддержкой МК на ARM. То что делал — не особо актуально для процессоров с MMU, в том числе x86.

Спасибо. Классный график :) Однако есть ещё некоторое количество "безымянных" опкодов. Ну то есть команда есть, а названия у неё нет. И документации как правило тоже нет. Но если знаешь — можно emitами в код вставлять. Но в целом я думаю они не более 1% инструкций

В 2017 исследователь перебрал всё пространство опкодов, и нашёл в Intel Core i7-4650U (вышедшем в 2013) несколько сотен недокументированных опкодов, не вызывающих #UD. Примерно четверть из них документировали «задним числом» (между 2013 и 2017).

Несколько сотен недокументированных опкодов среди нескольких тысяч документированных — это, похоже, намного больше 1%.
Кстати, тот же исследователь смог получить root доступ на Linux машине, используя недокументированные инструкции. Выступление называется «GOD MODE UNLOCKED — Hardware Backdoors in x86 CPUs».
Справедливости ради, эта «недокументированная фича» нашлась в процессоре VIA, а не Intel.
Там более интересная история, на самом деле. Этот режим не то, чтобы уж совсем не документирован. В документации о VIA написано достаточно информации для того, чтобы его выключить (на Wikipedia есть ссылки… но сама идея, что железяка, с завода, поставляется в режиме, когда в ней есть работающий backdoor… а впрочем — такой ли это уникальный случай? Чем это принципиально отличиается от поведения Samsung?
Интересно, сохраняется ли поведение этих опкодов от поколения к поколению?
Более того, в любой момент в процессор можно добавить некоторое количество новых… сделав его уникальным.
Pentium MMX вышел в 1997, как и Pentium II.
Для каждого года отмечен только один процессор.
Странно что он стоит прямо над «1995»
Ну… мясник так видит. Посмотрите где стоят числа 1978 и 2019… Что-то съехало при генерации картинки чуток.

У Вас ус отклеился, простите, ссылка сломалась… :-((((( хабрапарсер чертов

Хотел бы я знать, почему именно эти года там написаны.
В 1992 ведь ещё и Pentium не было, не то что MMX.
Вполне вероятно, что указали не дату начала выпуска серийного процессора, а к примеру, выпуска первых ES камней.
Как раз готовился к производству Pentium 60
Графики отражают бум индустрии компьютерных игр середины 90x (SSE+MMX) и криптографический «бум» конца нулевых (AVX)

Раз уж вы свели табличку, может и в текстовом виде её вставите?

FPU был всегда, то что он переехал в корпус немного сбивает ваш график
Пока не переехал в корпус, он не был частью x86.
С учетом того, что даже 8086 всегда так или иначе обрабатывал (передавал сопру или эмулировал с помощью исключений) команды FPU, x87 это изначально именно «a floating-point-related subset of the x86 architecture instruction set.» как написано в Вики
… либо не передавал и не эмулировал, если не было ни сопроцессора, ни рантайма для эмуляции.

Не говоря о том, что сопроцессор для 8086 не обязан был быть арифметическим, т.е. опкоды сопроцессора не обязаны были соответствовать операциям с плавающей точкой.
С учетом того, что даже 8086 всегда так или иначе обрабатывал (передавал сопру или эмулировал с помощью исключений) команды FPU
Не подскажите как выглядел ультра-супер-мега-модуль, позволявший процессору в 1978м общаться с кристаллом, расположенным в 1980м? Это ж просто мегатехнология какая-то!

То, что 8086 был выпущен с рассчётом на то, что в будущем, к нему будут выпущены сопроцессоры (не обязательно математические, был ещё и 8089… который, кстати, вышел раньше 8087, в 1979м) — не обозначает что все инструкции всех этих сопроцессоров нужно в список вносить…

P.S. 80387й тоже через два года после 80386го появился, кстати.
Именно так — проектировался «с рассчётом на то, что в будущем, к нему будут выпущены сопроцессоры». То, что 8087 был запущен в продажу в 80ом ничего не говорит о цикле разработки.

The 8087 was initially conceived by Bill Pohlman, the engineering manager at Intel who oversaw the development of the 8086 chip. Bill took steps to be sure that the 8086 chip could support a yet-to-be-developed math chip.

In 1977 Pohlman got the go ahead to design the 8087 math chip. Bruce Ravenel was assigned as architect, and John Palmer was hired to be co-architect and mathematician for the project.
en.wikipedia.org/wiki/Intel_8087#Design_and_development

Наличие/отсутствие позволяли обрабатывать исключения —
#NM—Device Not Available (No Math Coprocessor)
#MF—Floating-Point Error (Math Fault)


Хозяин, барин, конечно, но кажется если учесть эволюцию (увеличение числа) команд 8087 — 80187/80287 — 80387 — 487SX, то график стал бы красивее.
… опкоды сопроцессора не обязаны были соответствовать операциям с плавающей точкой.
Так и да, но… Но с идентичной структурой данных, адресацией и т.п. Поскольку основной процессор так же декодировал эти команды и управлял обращениями к ОЗУ.
Поскольку основной процессор так же декодировал эти команды и управлял обращениями к ОЗУ.
Не. Он тупо читал два байта. И выкидывал их. И всё.

Всё остальное делал сопроцессор. Он мог, в принципе, вообще что угодно делать — но любая инструкция была длиной в 11 бит…
Может, конечно, и память меня подводит, но поясните с точки зрения здравого смысла, как по вашему могли бы работать команды:

fld tbyte ptr [si+12]
fstp qword ptr [bp]
fild dword ptr [di+bx]
fabs


С «fabs», как бы, может быть по разному (ЕМНИП почти внутреннее дело сопроцессора), а с остальными? Кто регистры складывает для вычисления адреса, кто с длинной операнда в памяти разбирается, кто адрес(а) на шину выставляет, кто что декодирует?
Wikipedia содержит отдельный раздел, там всё описано.

Процессор декодирует команду (считая что первый байт — это опкод, второй — ModRM) и читает два байта, которые потом тупо выкидывает.

Сопроцессор за этим делом «шпионит» и, при желании, может использовать адрес и сами эти самые байты (они на одной шине сидят, всё что видит процесор видит и сопроцессор).

Всё остальное он делает независимо.
;) Спасибо за ссылку на напоминалку. Конечно, мой вопрос был почти риторическим, но я никак не мог вспомнить команду, которая работала немного по другому: «FSTSW AX».

А так, там же и написано ж, что:
  • Не «любая инструкция была длиной в 11 бит», а совместимая с декодированием modR/M, т.е. из этих 11 бит, совсем уж «любыми» могут только 3 бита;
  • Не «при желании», а система команд сопроцессора должна иметь и имеет такую структуру, что бы при mod != 11b обмен был необходим (даже если предположить некий гипотетический «кривой» сопроцессор, то обращения в память должны быть всё равно успешными);
  • Не просто «шпионит», а с учётом modR/M, т.е. с «пропуском» возможного цикла обмена с памятью для загрузки смещения.

Всё это вместе и можно назвать: процессор и сопроцессор совместно декодируют инструкции и процессор управляет обращением в ОЗУ (первым обращением). ;)
обращения в память должны быть всё равно успешными

Как обращения 8086 в память могут быть неуспешными?
И 8086/8087, при неудачном обращении к шине ISA, мог, в принципе, огрести NMI (ЕМНИП, память устройства могла же говорить, что её надо ещё подождать, но если дождаться не удалось — NMI. Вроде, как, контроль чётности тоже мог NMI. Что-то ещё...). Да и вообще, разработчики внешних устройств весьма креативные люди, так что прежде чем обращаться к их отображённым адресам, лучше семь раз подумать.

Ну а к процессорам/сопроцессорам 80286/80287, 80386/80387, надеюсь, вопросов нет ;)
Ну а к процессорам/сопроцессорам 80286/80287, 80386/80387, надеюсь, вопросов нет ;)
К этим нет — там и протокол и вообще всё параллельно разрабатывалось. В случае с 80286/80287 — вообще всё одновременно, у них даже мануал один на двоих, а 80387й — да, появился позже, но там просто ускорили разрабтку 80386го в какой-то момент.
Интересно, что у 80287 есть команда fsetpm, а 387 она уже ненужна.
«Концепция поменялась». Тупо ножки и транзисторы перестали экономить и стало возможным информацию о переходе в защищённый режим передавать по-другому. Без помощи со стороны процессора…
Конечно, мой вопрос был почти риторическим, но я никак не мог вспомнить команду, которая работала немного по другому: «FSTSW AX».
Вот только 8087 этой инструкцией не обладал. Она в 80287м появилась… и даже понятно почему.

Всё это вместе и можно назвать: процессор и сопроцессор совместно декодируют инструкции и процессор управляет обращением в ОЗУ (первым обращением). ;)
Ну можно и так, наверное. Но факт в том, что 8087й появился только через два года, после выпуска 8086го. То есть какой-то задел, под него, конечно, сделали, но окончательного понимания как всё будет работать ещё не было.
Почему-то казалось, что наращивание числа потоков затормозит эту тенденцию, а нет.

Так. У меня вопрос. TL;DR. Прошу прощения, если что


Так вот. Считалось количество реально разных инструкций (т.е., например, все вариации MOV за одну команду) или количество разных вариаций оп-кодов (т.е., например, все вариации одной команды с разными опкодами — разные). И как считались префиксы (repne всякие)?

Считались мнемоники (написано прямо под графиком). Префиксы REPxx считались одной мнемоникой.
Такой вопрос: SSE, MMX, AVX — с учетом того, что это более или менее про одно и то же, а именно SIMD, был ли практический смысл называть их абсолютно разными аббревиатурами? Логичнее было бы версиями обойтись? Это чисто маркетинг?
У них длина регистров разная.
Условные SSE64, SSE128, SSE256 разве не были бы более понятными и более приемственными?
SSE и AVX — да, а у MMX совсем другая архитектура.
И даже и SSE и AVX — это не одно и то же. Когда появился AVX почти каждая SSE инструкция получила своего «AVX-двойника»: такую же инструкцию — но с тремя операндами. А чтобы всех окончательно добить — использование SSE и AVX инструкций «вперемешку» здорого просаживает производительность.

Вот зачем была нужна вторая «фича» — до сих пор никто не знает, технически ничто не мешало сделать эти инструкции преемниками друг друга, просто с разным количеством аргументов.

P.S. Кстати использование AVX512 тоже просаживает проиводительность AVX2-кода, но при этом имена похожие… в общем какие-то потуги маркетологов…
Вряд ли. Скорее потуги маркетологов и желание получить ещё 1% на каком-нибудь бенчмарке. Ранние AVX-процессоры работали с 256-битными регистрами как с парой 128-битных. И если вам 256-битные инструкции не нужны — можно было их «расцепить» и использовать как два 128-битных регистра.

Что было и логично, так как работать с «полными» 256-битными регистрами более-менее беспроблемно можно только в AVX2, в первой версии AVX много ограничений. Программа, использующая AVX, могла об желании работать в таком режиме «явно сообщить» процессору, вызвав VZEROUPPER. А программа, использующая SSE — про AVX ничего не знает и нужен был другой способ.

Ну вот… нахимичили… теперь расхлёбываем.
UFO just landed and posted this here
В двух словах про false dependencies (не специфично для SSE/AVX — с любыми регистрами, к которым можно обращаться по частям, та же фигня):

  • инструкция №1 заполняет R целиком;
  • инструкция №2 читает R;
  • инструкция №3 заполняет половинку R и оставляет вторую половинку нетронутой.

В этом раскладе невозможно запустить на выполнение №3 параллельно с №1, даже если нетронутая половинка R в дальнейшем использоваться не будет.

Именно поэтому в x64 прописали, что 32-битные операции обнуляют старшую половинку регистра. (Для сравнения, во время работы над 80386 о возможности OoO не подумали, и поэтому 16-битные операции оставляют старшую половинку 32-битного регистра нетронутой.)
UFO just landed and posted this here
Ренейминг эту проблему не решает, потому что в дальнейшем может встретиться инструкция, читающая R целиком — а значит, №3 должна работать над тем же самым физическим регистром, что №1 и №2.
Здесь нужно заметить, что, строго говоря, можно разбить все регистры на два, и оперировать с ними независимо. Однако извести эти паразитные зависимости до конца всё равно нельзя, а схемотехнику процессора они сильно усложняют.

Для 8 битных регистров это делали с Pentium II (скорее всего и сейчас ещё делают).

Помните байку про то, что Pentium Pro плохо работает на 16-битных программах, но отлично на 32-битных? Вот это вот оно.

Там речь не про 16-битные программы, а про программы, использующие %al/%ah и вот это вот всё.

Но для «упрощения» шла речь о 16-битных программах (MS-DOS и Windows 95).
После Pentium 4 делят биты условий во Flags на разные «регистры». Видно по тому, что правило «не используйте INC/DEC — у него false dependency, потому что не меняет CF!» перестало быть важным для Core.
У SSE и AVX хотя бы регистры те же. Трехоперандные команды сильно облегчают задачу планировщика и, наверное, дорого обходится переключение между режимами (перезагрузка конвейера и т.п.)
Как раз фишка в том, что замедление происходит из-за разных регистров! У SSE регистры 128-битные, у AVX — 256-битные, расширенные. При этом операции с SSE старшую половину не меняют, а AVX — либо записывает туда что-то полезное, либо нули.

В результате SSE2 инструкции (с учётом переименования регистров) оказываются сложнее, чем AVX, но главное — возникают «паразитные связи» между инструкциями. Потому что если у вас в один и тот же регистр AVX инструкция чего-то пишет, а потом с ним работает SSE инструкция… то, внезапно, результат работы SSE инструкции оказывается зависимым от результата работы AVX-инструкции и впараллель их исполнять нельзя.

Прелестно, да? Что мешало сделать SSE-иструкции просто копией AVX-инструкций с двумя операндами?
Да я не оправдываю — просто предположил. Мне когда-то пришлось написать несколько функций на SSE — помню очень «облизывался» на, тогда еще только анонсированное, AVX — в нескольких местах трехоперандные команды позволили бы написать гораздо более красиво и лаконично.
внезапно, результат работы SSE инструкции оказывается зависимым от результата работы AVX-инструкции и впараллель их исполнять нельзя.

А зачем бить себя лопатой по голове использовать AVX и SSE одновременно? В AVX есть 128-битные команды, аналогичные SSE2. Старшая часть отключается, если не нужна.
Также имеется VZEROUPPER чтобы сбросить зависимость.

Что мешало сделать SSE-иструкции просто копией AVX-инструкций с двумя операндами?

Простите что? SSE появился в 1999г, AVX в 2011.
Не очень понятно о каком легаси идёт речь, если SSE используется параллельно с AVX.
software.intel.com/content/www/us/en/develop/articles/intel-avx-state-transitions-migrating-sse-code-to-avx.html
To бишь, согласно иллюстрации в документе Intel, мы переходим из состояния B в состояние C.
Operating in State C is undesirable.

Что же касается компиляции легаси кода, то SSE интринсики автоматически преобразуются в AVX компилятором.
zig.godbolt.org/z/BkFrTE
К примеру в 2002 году был написан кусок кода в котором торчали ассемблерные вставки (мы ведь понимаем, что врядли такое будут делать компиляторы) для ускорения каких-то вычислений, а через лет пятнадцать — было решено ещё что-нибудь отоптимизировать. И по результату вполне может получиться «отрицательный рост» производительности благодаря тому, что очередной кодер тупо вспомнил о том, что «вот есть такая крутая инструкция, она здесь просто прекрасно будет смотреться». Сам в поисках наиболее быстрого способа копирования памяти такое чудил, правда в те времена, когда кроме SSE ничего другого и не было.
Скорее всего тот код уже превратился в балласт и его давно пора удалить.
Ассемблерные вставки — зло. Используйте интринсики, либо пишите нормальный асм код.
Если ваш кодер плюёт на рекомендации Интел и расчесывает котов против шерсти не соблюдает правила, он ССЗБ.
Ещё раз напомню, что эта «страшная» проблема лечится одной инструкцией. Написав этот коммент, вы приложили на порядок больше усилий чем требует решение проблемы.
Ещё раз напомню, что эта «страшная» проблема лечится одной инструкцией.
Какая интсрукция? MAGICBEERUSER2? Или как?

Как вы собираетесь AVX-код на процессорах без AVX запускать?

У нас, к примеру требование: программа должна поддерживать все процессоры с поддержкой SSE2 и более новые.

Куда и какую команду вставить чтобы реализация инструкций на SSE (а не весь код требует 256 битных регистров, там есть даже и скалярные вычисления, представьте), вдруг, внезапно, стала нормально работать везде?
Инструкция VZEROUPPER.

Если в вашем коде нет AVX-инструкций, а есть только SSE, то нет никаких проблем — проблемы появляются только от их смешения.
Проблема в том, что их смешение — это естественный результат применения DRY-принципа.

Если ваша прогремма должна, внезапно, поставляться заказчику (да и даже выполняться на кластере, где есть компьютеры с AVX и без) — то логично тот код, которому AVX не нужен писать на SSE и не умножать сущности без необходимости.

Но тогда вам придётся на каждом переходе требовать, чтобы использовался VZEROUPPER. И в результате, так как компиилятор понятия не имеет — использует ваша программа SSE-код или нет, он эту инструкцию пихает во все места. И он будет это делать даже без вашего желания. Да, можно его стукнуть по башке… но вы реально верите что вот все-все должны это делать и делают? Я даже не знаю как в MSVC это сделать в принципе (поиск показывать только кучу несчастных, безуспешно пытающихся его научить так делать).

Прелестно, не так ли? Вы и после вот всего этого будете утвеждать, что разработчики AVX вовсе-то и не обосрались?
Если ваша прогремма должна, внезапно, поставляться заказчику (да и даже выполняться на кластере, где есть компьютеры с AVX и без)

Если у вашей минимальной целевой платформы нет AVX, значит используются чипы 9-летней давности и производительность никого не интересует.
Это решить проще всего — используйте только SSE2.
Если конечно маркетинг не продаёт AVX код как фичу.

Но тогда вам придётся на каждом переходе требовать, чтобы использовался VZEROUPPER

На каком переходе? Она нужна в конце вычислений с использованием 256-битного AVX.
Если вы используете 128-битный AVX (который переводится из SSE компилятором), скорее всего уже
проц находится в состоянии A (в вышеупомянутой таблице).

он эту инструкцию пихает во все места.

Вот видите, компилятор сам заботится о вас, чтобы у вас не тормозил код.
В чём, собственно, проблема?

Несмотря на то что в Skylake убрали столл при переключении состояния, если вы не сделаете vzeroupper, будет другая «беда».

For what it's worth, the vzero family is still very much needed on Skylake. The big one-time state transition penalty has disappeared, but was replaced with the ongoing penalty of blending the high half of the register for all all the non-VEX encoded instructions.

That penalty if often huge, perhaps a 2x to 6x slowdown, and it never ends while you run non-VEX code. So you very much need to do the zeroing on Skylake — not for «consistency» but for performance. This has been biting people repeatedly in weird and wonderful ways — the old one-time penalty probably occurred here too, but as a small one-time it would have never been noticed.


Вы и после вот всего этого будете утвеждать, что разработчики AVX вовсе-то и не обосрались?

А, ну всё понятно. Вас интересует поиск виноватых…
Если у вашей минимальной целевой платформы нет AVX, значит используются чипы 9-летней давности и производительность никого не интересует.
Если у вас нет мозгов — то ваше мнение никого не волнует.

Представьте себе — не все программы в мире это игры, где CPU должно хватать для рассчёта картинки за 1/60 секунды.

Какой-нибудь Floyd–Steinberg вполне может быть достаточно медленным даже на процессоре с AVX2, однако если ваш графический редактор не будет работать на компьютерах без оного — то вас, извините, не поймут-с.

Photoshop до сих пор требует только SSE2 — и ничего более.

На каком переходе?
При мереходе из функции в функцию, однако.

Вот видите, компилятор сам заботится о вас, чтобы у вас не тормозил код.
В чём, собственно, проблема?
Проблема в том, что цена VZEROUPPER — нифига не нулевая. И наличие его там, где оно не нужно (также, как и его отсутствие там, где нужно) — приводит к замедлению.

Несмотря на то что в Skylake убрали столл при переключении состояния, если вы не сделаете vzeroupper, будет другая «беда».

For what it's worth, the vzero family is still very much needed on Skylake. The big one-time state transition penalty has disappeared, but was replaced with the ongoing penalty of blending the high half of the register for all all the non-VEX encoded instructions.

That penalty if often huge, perhaps a 2x to 6x slowdown, and it never ends while you run non-VEX code. So you very much need to do the zeroing on Skylake — not for «consistency» but for performance. This has been biting people repeatedly in weird and wonderful ways — the old one-time penalty probably occurred here too, but as a small one-time it would have never been noticed.
А вы не заметили, что мы, как бы, ровно эту вашу «беду» тут и обсуждаем? То, что было в Sandy bridge — это, конечно, вообще ни в какие ворота не лезет, но вот это вот идиотское отличие SSE от AVX — по прежнему является проблемой, как вы сами-таки и откопали…
Photoshop до сих пор требует только SSE2 — и ничего более.

Как видите, они следуют моему совету. Отнюдь не потому что у них «нет мозгов» :)

При мереходе из функции в функцию, однако.

У вас весь код состоит из перемешанных SSE и AVX функций?

Проблема в том, что цена VZEROUPPER — нифига не нулевая.

А какая? Должна быть в районе нескольких тактов.

А вы не заметили, что мы, как бы, ровно эту вашу «беду» тут и обсуждаем?

Это ваша беда — у меня нет таких проблем.
Обсуждаем мы «легаси код 2002г» и смешивание инструкций разных наборов, что крайне не рекомендуется делать разработчиком архитектуры.
Проблема в том, что цена VZEROUPPER — нифига не нулевая.

А какая? Должна быть в районе нескольких тактов.
Даже один такт может вполне ощутимо просаживать производительность. Современные процессоры мало того, что могут исполнять 3-4 инструкции за такт, так они ещё и спекулятивно могут исполнять код в других функциях (для этого есть специальные оптимизации).

Обсуждаем мы «легаси код 2002г»
Серьёзно? То есть вы можете убедить вышеупомянутом примере MSVC не геренить лишний VZEROUPPER? Расскажите как — после этого можно будет что-то обсуждать. Там, как бы компилятор от декабря 2019го… свеженький такой.

и смешивание инструкций разных наборов, что крайне не рекомендуется делать разработчиком архитектуры.
Угу. И мы даже выясниль — костылём к какому именно костылю этот идиотизм был порождён… Вот только платите вы за всё это даже на самых соврменных процессорах и даже там, где никакого SSE нет и не будет (нет, clang/gcc позволяют от костылей отказаться… MSVC, похоже, нет).
Даже один такт может вполне ощутимо просаживать производительность.

Смешно.

После условных 10000 тактов на выполнение цикла, этот 1 такт прямо так ощутимо будет просаживать производительность вашей программы :)
Согласно таблицам Фога, на процессорах Intel vzeroupper и занимает 1 такт. Не думаю, что вы считаете количество обращений к памяти. Это подотчётная операция у вас? Ибо, с такой заботой об каждом такте, она должна быть таковой.

Современные процессоры мало того, что могут исполнять 3-4 инструкции за такт

А вы измеряли сколько в реальности выполняется?

sophisticated CPU cores actually sustain an average of slightly more than one instruction per cycle when executing many programs.

Или вот отрывок из документации по vtune:
Modern superscalar processors issue up to four instructions per cycle, suggesting a theoretical best IPC of 4. But various effects (long-latency memory, floating-point, or SIMD operations; non-retired instructions due to branch mispredictions; instruction starvation in the front-end) tend to pull the observed IPC down. A IPC of 1 is generally considered acceptable for HPC applications but different application domains will have very different expected values.

То есть вы можете убедить MSVC не геренить лишний VZEROUPPER?

А смысл? Его нужно генерить в вашем случае.
Это рекомендация Интел.

Его не нужно было бы генерить, если бы вы использовали AVX-128. Моя целевая платформа (кстати с довольно дорогим vzeroupper) предполагает наличие AVX, соответственно вашей проблемы у меня нет, как я уже говорил.

И мы даже выясниль — костылём к какому именно костылю этот идиотизм был порождён

Операции над AX точно так же не очищают EAX. Искать виноватых — последнее дело.

MSVC, похоже, нет

С выходом 10-го clang, пользоваться MSVC по большому счёту и не нужно.
Операции над AX точно так же не очищают EAX. Искать виноватых — последнее дело.

Зато очищают верхнюю половинку RAX :-P
Ни у Intel ни у AMD операции над AL, AX не очищают верхнюю половинку RAX. Можете взять ассемблер и проверить.

Тут
#include <stdio.h>
#include <stdint.h>
int main() {
  uint64_t a = 0x123456789abcdef0;
  asm(
    "mov $3, %%bl\n\t"
    "mov %%bl, %%al"
    : "=a" (a) : "0" (a) : "rbx");
  printf("%lx\n", a);
}

Запускаю:
123456789abcde03



И это по-своему логично, потому что если операции над AL, AX обязаны сохранять старшие биты EAX (8-31 и 16-31 соответственно), и (false) dependency всё равно присутствует — то зачем их ломать ради очистки RAX?

А вот 32-битные операции — да, очищают — там эту зависимость можно развязать, чем и воспользовались.

Обратите внимание, что компиляторы для переноса 8- и 16-битных значений в регистр жёстко предпочитают использовать не простой mov, а movsbl, movzbl, movswl, movzwl и т.п. (в обозначениях AT&T) — именно для исключения этих зависимостей самым дешёвым доступным способом…
Обратите внимание, что компиляторы для переноса 8- и 16-битных значений в регистр жёстко предпочитают использовать не простой mov, а movsbl, movzbl, movswl, movzwl и т.п. (в обозначениях AT&T) — именно для исключения этих зависимостей самым дешёвым доступным способом…
Только 16 бит. Из-за MS-DOS и Windows 95 в Pentium II сделали AL и 3/4 AX отдельными регистрами, а дальше — на это компиляторы заложились, потому это теперь не убрать.
Переключите с clang на gcc и увидите movzx и при 8-битном аргументе.
Про clang & icc принято, но всё равно странновато и никто не мешает тут не делать потенциальные диверсии.
А что такое «3/4 AX»?
Биты 8-31 от EAX?
А что такое «3/4 AX»?
Биты 8-31 от EAX?
Угу. У них была проблема в том, что в MS-DOS активно используется API, где AL/AH, BL/BH, CL/CH и DL/DH рассматриваются как независимые регистры. Из-за этого архитектурно более совершенный Pentium Pro оказывался по скорости едва-едва быстрее Pentium. При цене чуть не на порядок большей.

В Pentium II эти регистры начали рассматривать как независимые.

Про clang & icc принято, но всё равно странновато и никто не мешает тут не делать потенциальные диверсии.
Если учесть, что ICC — это прямо «компилятор от производителя CPU», то, наверное, так и надо…

А вообще — у них заметно разные «подходы». Вот тут можно увидеть. Clang пытается за счёт использования дополнительных регистров код сделать проще, icc довольно быстро отказывается от 8-битных регистров и пытается за счёт битовых оптимизаций что-то сделать… gcc тупо «сливает».

P.S. В 64-битном режиме это меньше заметно, там же 16 8-битовых регистров (правда включая SPL). В 32-битном режиме — это беда…
Это подотчётная операция у вас? Ибо, с такой заботой об каждом такте, она должна быть таковой.
Знаете, если у вас весь код пишется с одинаковым подотчётным тщанием — то мне вас просто жаль.

Потому что у нормальных людей, у которых в голове мозги, а не пиво, в одной и той же программе может быть код на Python (какая там производительность — знаете без меня) и код, в котором не то, что каждый такт учтён, но даже проверена раскладка по испоняющих устройствам для разных CPU.

Всё зависит от того, что это за код и где он используется.

То есть вы можете убедить MSVC не геренить лишний VZEROUPPER?

А смысл? Его нужно генерить в вашем случае.
Это рекомендация Интел.
Ах, ну да. Как же я мог забыть. Нужно просто соблюдать рекомендации — и всё будет чики-пуки. А если ништяки с неба не сыплются, то нужно просто рекомендации тщательнее исполнять.

Его не нужно было бы генерить, если бы вы использовали AVX-128.
А кто тут у нас писал Что же касается компиляции легаси кода, то SSE интринсики автоматически преобразуются в AVX компилятором? Быстро же у вас сменилась «линия партии».

Операции над AX точно так же не очищают EAX и вызывают partial register stall.
А вот операции с EAX, внезапно, так не делают. Старшая половина RAX всегда либо обнуляется.

Искать виноватых — последнее дело.
Наоборот. Когда кто-то где-то вырезает «гланды автогеном через задницу» — это первое, что нужно сделать. И понять почему. А потом уже можно решить — что с этим делать. Может уже пора и перестать, а может нужно и, наоборот, продолжить это делать — если тому есть веская причина.

С выходом 10-го clang, пользоваться MSVC по большому счёту и не нужно.
По-моему это рекорд: тут у вас «линия партии» сменилась посередине одного сообщения.
UFO just landed and posted this here
При учёте того, что почти весь современный около-GPL софт (за редкими исключениями) я совершенно спокойно собираю и запускаю на Pentium II — то лучше всего будет сделать на MMX. И не забыть предусмотреть варианты сборки на SPARC/ARM/MIPS/Alpha и прочих. То есть сделать недурной такой подраздел в ./configure на распознавание/выбор процессора.
Я его понял так: вот мы расширили регистры и можем предполагать любую семантику для старшей части — что мешает сделать, что старые инструкции (SSE) чистят всё выше бита 127?
И вот поиском на такую тему нахожу:
The history is this: The extension of vector registers from 128 to 256 bits caused a problem when legacy Windows device drivers saved only the lower 128 bits of the new 256-bit registers. This problem was solved in a rather complex way.
и дальше описание того, что уже знаем.
«Семь бед — один ответ! Костыль и велосипед!» (tm)
Дальше по ссылке пояснение, что виноваты не «legacy drivers», а Win64 ABI, предписывающее сохранять XMM-регистры. Во время создания этого ABI ещё не предполагалось, что у XMM-регистров появится «верхняя половина», которую уже скомпилированный код сохранять не сможет.
OK, пусть Win64 ABI не сохраняет верхние половины. Мы знаем, что оно их не сохраняет, но у нас корректное состояние с сохранением нижних половин (всё SSE их сохраняет, а верхние части могут обнулиться или иначе измениться). Что мешает зафиксировать факт такой работы текущего ABI и не насиловать процессор? Всё равно если надо сохранять YMM регистры целиком — это будет другой ABI.

Объясните, а то что-то не сходится.
Если не менять ABI, то получится, что XMM-регистры сохраняются дважды: вызывающей стороной вместе с верхней половиной, и вызываемой стороной без верхней половины. Это не катастрофично, но очень странно.
Поэтому ABI собирались обновить обратно-совместимым образом: вызываемая сторона либо сохраняет верхние половинки, либо не трогает их, и тогда они остаются без изменения. Хотя судя по тому, что я вижу в MSDN прямо сейчас, такого обновления ABI так и не произошло.
> вызывающей стороной вместе с верхней половиной, и вызываемой стороной без верхней половины. Это не катастрофично, но очень странно.

Это неизбежно. И тут Intel мог бы помочь, например, прямым доступом к верхним половинам… хотя смысла всё равно нет с текущим стилем железа — для такой задачи, где нужен AVX256, сохранить на 16 байт больше из одного регистра — копейки.

> такого обновления ABI так и не произошло.

Они и не могли сделать такое обновление. Костыль принципиально уже не мог сработать и не сработал.
Они и не могли сделать такое обновление. Костыль принципиально уже не мог сработать и не сработал.

Думаю, что при добавлении в MSVC поддержки AVX вполне могли бы. (Сейчас, конечно, уже не могут.)
Дальше по ссылке пояснение, что виноваты не «legacy drivers», а Win64 ABI, предписывающее сохранять XMM-регистры.
Нет.

Во время создания этого ABI ещё не предполагалось, что у XMM-регистров появится «верхняя половина», которую уже скомпилированный код сохранять не сможет.
То, что он эти части YMM-регистров не может сохранить или восстановить — это полбеды. В конце-концов если они потеряны никакой VZEROUPPER не поможет.

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

Именно это создаёт «паразитную связь» между AVX-кодом, работающим в одной процедуре, через SSE-код, который сохраняет младшие половинки и только их и восстанавливает и AVX-кодом уже в другой процедуре.

Если бы SSE-код эти страршие половинки бы обнулял — весь этот «танец с бубнами» был бы не нужен.

К сожалению иначе как вредитеством я это решения назвать не могу. Ну потому что это ж было через много лет после фиско Pentium Pro — та история должна была бы остаться у кого-то в мозгах!

А VZEROUPPER — это уже попытка частично это вредительство смягчить…
Проблема в том, что вы нашли кой-чего-совсем-не-то.

Подумайте для начала — почему, вдруг, процессор оказался в таком состоянии, что VZEROUPPER может чему-то помочь?

Ответ: потому что кто-то сохранил значение 128 бит в стеке, а потом восстановил (как вы прочитали в той статье) с помощью SSE инструкций!

А что было бы, если бы, вместо этого, использовались бы AVX128 инструкции (или если бы SSE инструкции вели себя так же, как AVX128 инструкций). Ой, глядика-ты: проблема бы рассосалась и исчезла!

Паразитных зависимостей не было бы изначально, никакой VZEROUPPER был бы не нужен.

и дальше описание того, что уже знаем.
«Семь бед — один ответ! Костыль и велосипед!» (tm)
Ага. Только вот этот «костыль и велосипед» — это уже как раз следствие того, что SSE ведёт себя не как AVX128.

А вот какую проблему это «расчщепление сущностей» помогло-таки решить?
> Подумайте для начала — почему, вдруг, процессор оказался в таком состоянии, что VZEROUPPER может чему-то помочь?

Вам незачем мне тут возражать — я точно так же говорю, что проблема возникла, когда решили, что SSE на расширенных регистрах должно не трогать старшие 128 бит, при том, что это была вторая половина 2000-х и последствия уже все знали.

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

> Проблема в том, что вы нашли кой-чего-совсем-не-то.

А что именно «то»?
Ваши же слова рядом:
Нет, обсуждаемая по той ссылки проблема была рождена как раз чьим-то идиотским решением не обнулять старшие половинка 256-битных регистров при использовании SSE инструкций.


так вот — Intel, возможно, и идиоты, но не кретины, в медицинском смысле. Что именно было тут причиной? Я перебрал возможные ситуации со своей точки зрения, и единственное предположение тут следующее: если драйвера (это слово там прозвучало не зря?) используют SSE, и умеют при этом сохранять модифицированные ими регистры, но только командами SSE (а не каким-нибудь XSAVE), и нет горизонтального переключения между соседними процессами (это важно — иначе не поможет), или же это горизонтальное переключение сделано системным кодом, который уже YMM-aware, а драйвера — ещё нет — то вариант «делаем, чтобы никакое SSE не трогало верхние половинки» тут поможет. Пенальти на переключение между State B и State C на входе/выходе сисколла или прерывания — не так уж и страшно, с их точки зрения.

Многовато натяжек (всё равно непонятно, почему нельзя было на границе сисколла/прерывания явно отработать проблему), но «если отбросить всё невозможное — остаётся реальное». Или у вас есть другие идеи?
Многовато натяжек, но «если отбросить всё невозможное — остаётся реальное». Или у вас есть другие идеи?
Похоже они не нужны. Да, там по ссылки есть сообщение от чувака из Intel, который ровно про это и говорит: да, у нас есть драйвера, которые трогают SSE (руки бы оторвать таким драйверописателям) и нам придётся «устроить всем кузькину мать».

Которую мы теперь будем разсхлёбывать вечно. Ну или пока от испоьзования SSE не откажемся совсем, по крайней мере.

Класс.

Ну спасибо хоть объясниснили в чём беда, а то я уж совсем испугался за разум товаристчей из Intel: Theoretically a new OS could take care of this for the ISR, but we didnt want to penalize users of the legacy architecture and could not mandate a major OS re-write.

Жуть. Хоть бы флаг в процессоре сделали бы, чесслово. Собственно два флага: один для привелегированного кода, другой для непривелигированного. И всё. И нет проблем.

Никто бы про эту разницу между SSE и AVX (кроме драйверописателей) даже не задумывался бы…

Тогда хотя бы те, кого это не касается (все пользователи Линукс, собственно), жили бы спокойно…
Так а всё-таки, что создатели «кривых драйверов» сделали не так?
Не сохраняли верхнюю половину регистров, которой ещё не существовало?
> Так а всё-таки, что создатели «кривых драйверов» сделали не так?

На вопрос типа «ты перестал пить коньяк по утрам?» сложно ответить конструктивно. А ваш вопрос именно такого типа. При чём тут авторы драйверов, когда проблема создана на уровне процессора?

Решить её можно было несколькими методами, но почему-то выбран оказался «лучший из худших».
Ну, конкретно khim предложил «руки бы оторвать таким драйверописателям», а я не понимаю, за что.
Ааа. Я думаю, это недовычищенное при редактировании. Я его вначале и не заметил.
За использование SSE в драйверах. В 99% случаев никакой SIMD в драйверах нафиг не нужен, потому что процессор почти всегда всё равно быстрее, чем железо.

А зато если SSE регистры не используются, то их не нужно и сохранять.

Если же ну вот очень-очень-очень надо, прям край как надо, ускорение ожидается космическое… можно использовать инструкцию, которая, внезапно, как раз для этого и предназначена (и появилась в Pentium II, ещё когда SSE первой версии в проекте был… так что не надо про то, что «мы ж не знали» — знали, просто не думали).

Подобные дискуссии среди разработчиков Linux — ни разу не редкость и, в результате, в Linux в драйверах ни SSE, ни AVX не используются (за исключением драйвера RAID).
При чём тут авторы драйверов, когда проблема создана на уровне процессора?
Проблема не создана на уровне процессора. Она создана на уровне драйверов. Сохраниение контекста (включая SSE регистры!) процессором осуществляется с помощью команды FXSAVE, которая, внезапно, появлась аж в Pentium II (то есть до изобретения SSE даже).

Однако некоторые ушлые драйверописатели обнаружили, что это ж долго — все рагистры-то сохранять! А давайте мы парочку регистриков тихо-и-незаметно куда-нибудь сохраним, а потом восстановим… никто ж ничего не заметит, ведь правда?

Ну а потом, как мы выяснили, и родилось вот это вот идиотское решение с явным разнесенеием SSE и AVX инструкций и «штрафом» за их перемешивание…
Хм, но ведь и FXSAVE не сохраняет верхние половинки?
Если нет разницы, зачем платить больше?
Хм, но ведь и FXSAVE не сохраняет верхние половинки?
Так она их не сохраняет потому что драйвера эту инструкцию не используют!

Научить сохранять YMM'ы в 32-битном режиме (где самое лютейшее легаси и обитает) — вообще без проблем, в 64-битном — можно было бы прописать в какой-нибудь MSR адрес процедуры, которая бы YMM регистры куда-нибудь записывала бы.

Главное — сохранение и восстановление состояния выполнялись бы настолько медленно (62+68 тактов = 130 тактов на Pentium II), что вся эта конструкция «яйца бы выеденного не стоила». Применялась бы в трёх всем известных драйверах, как в Linux, и всё.

Вообще вся эта попытка выиграть что-то под Windows и MacOS — это какой-то вечный забег по граблям. В духе «а давайте мы для экономии памяти научим ядро часть своего кода выгружать… а лет через 10 обнаружим, что у нас и невыгружаемая часть теперь больше, чем вообще всё ядро в Linux».

Про большую коллекцию граблей, которую собрала MacOS из-за попытки вписаться в 128K в 1984м году есть даже отдельная статья в Wikipedia.

Но, как известно уроки истории заключаются в том, что люди ничего не извлекают из уроков истории — тут мы это видим в очередной раз.

Развитие идёт по спирали, как известно… и каждое следующее поколение, получил от граблей фингал под глазом и обматерив предшественников всеми нехорошими словами, какие удалось вспомнить, тут же разворачивается и аккуратно покрывает оставшуюся за ними дорожку всё теми же самыми граблями.

А то, вдруг, следующее поколение без фингалов под глазами окажется… непорядок.
> Главное — сохранение и восстановление состояния выполнялись бы настолько медленно (62+68 тактов = 130 тактов на Pentium II)

Это, кстати, для общих затрат даже на вертикальное переключение — не очень много — всё равно все регистры сохранять-восстанавливать, а если делать это одной порцией в память, то самые серьёзные затраты с торможением по памяти будут обойдены. Не восстанавливать все пользовательские регистры опасно утечкой данных, потому что в стандартном соглашении вызова те регистры, что args + result + scratch, никто не чистит, а в callee-saved, наоборот, может что-то лишнее притечь ядру… опасно.
ARM при вертикальных переключениях сам заменяет все регистры.
Спрашивается, что мешало AMD и Intel при реализации x64 сделать так же? Вопрос обратной совместимости тогда не стоял, а стольких проблем удалось бы избежать.
ARM при вертикальных переключениях сам заменяет все регистры.
Это кто ж вас так жестоко обманул? На картинку, хотя бы, гляньте. В большинстве режимов он меняет буквально пару регистров и только в FIQ — половину (и то не все).

Все регистры менял 8080… уже даже Z80 не мог себе этого позволить.

Спрашивается, что мешало AMD и Intel при реализации x64 сделать так же?
Дык никто не мешал. Кроме здравого смысла. Прерывание — оно, знаете ли… прерывает. И потому должно работать быстро. Соответственно обычные регистры x64 сохраняет, а x87 и SSE — нет. Ну просто никому и в страшном сне не могло присниться, что при обработке прерываний кто-то решит, внезапно, использовать SSE.

Но… нет такого идиотизма, который не могут совершить разработчики в погоне за требованиями маркетологов! Если у вас есть кто-то вроде Линуса, кто посылает маркетологов «в пешее эротическое» — всё будет в порядке. Если нет — ну вот и получим то, что получили…
Дык никто не мешал. Кроме здравого смысла. Прерывание — оно, знаете ли… прерывает. И потому должно работать быстро.

Время, занимаемое переключением регистров, не зависит от числа переключаемых регистров, потому что меняется только маппинг, самое содержимое регистров никуда не перекачивается.

Но… нет такого идиотизма, который не могут совершить разработчики в погоне за требованиями маркетологов! Если у вас есть кто-то вроде Линуса, кто посылает маркетологов «в пешее эротическое» — всё будет в порядке. Если нет — ну вот и получим то, что получили…

Драйвера под Windows проверяются в WHQL, и Windows не загружает их без подписи, удостоверяющей, что драйвер проверен.
Время, занимаемое переключением регистров, не зависит от числа переключаемых регистров, потому что меняется только маппинг, самое содержимое регистров никуда не перекачивается.

В смысле? Т.е. Вы хотите сказать, что при переключении контекста НИКОГДА не сохраняется содержимое регистров в ОЗУ? Или про то, что прерывания != переключение контекста? Или про то, что в процессоре есть дофигища регистров, скрытых от пользователя (программиста), на которые можно что-то там намаппить?

Вы хотите сказать, что при переключении контекста НИКОГДА не сохраняется содержимое регистров в ОЗУ?

Если процессор не сохраняет регистры сам, то их приходится сохранять программисту в ОЗУ.

Или про то, что в процессоре есть дофигища регистров, скрытых от пользователя (программиста), на которые можно что-то там намаппить?

Конечно есть.
Конечно есть.

Я, наверное, не совсем правильно выразился. Т.е. существование их мне известно и я могу его… как бы… обосновать технически. Вопрос в том ДЕЙСТВИТЕЛЬНО ли они участвуют в означенных процессах ("сохранение регистров при прерывании" и "сохранение контекста при переключении процесса") или это побочный эффект.


И это мы не затрагиваем еще режимы работы вроде SMM.....

Вопрос в том ДЕЙСТВИТЕЛЬНО ли они участвуют в означенных процессах («сохранение регистров при прерывании» и «сохранение контекста при переключении процесса») или это побочный эффект.
Это — не побочный эффект, но сознательное архитектурное решение. От которого, внезапно, все отказались в современных архитектурах. Так как это — существенное усложнение всей конструкции без каких-либо плюсов. В оригинальной, 32-битной, архитектуре ARM IRQ сохранял 3 регистра, FIQ сохранял 8, на Arrch64 — в обоих случаях сохраняется парочка, остальным занимается OS.

И это мы не затрагиваем еще режимы работы вроде SMM.....
С SMM всё, как раз, гораздо проще: он под конкретный процессор затачивается, в случае существенного архитектурного обновления — может потребоваться обновления firmware всё равно.
Время, занимаемое переключением регистров, не зависит от числа переключаемых регистров, потому что меняется только маппинг, самое содержимое регистров никуда не перекачивается.
В физическом мире ничего не бывает «бесплатно». Если вы добавляете в процессор систему маппинга регистров, то у вас усложнается стадия декодирования и, что ещё хуже, она начинает зависеть от режима, в котором находится процессор… в общем всё это далеко небесплатно. Вспомните как и почему sysenter появился.

Драйвера под Windows проверяются в WHQL, и Windows не загружает их без подписи, удостоверяющей, что драйвер проверен.
А толку? WHQL — это просто некоторый набор тестов. Вы не поверите на что свособно воображение драйверописателей, у которых с одной стороны есть одна задача (пройти тексты), с другой другая (удовлетворить хотелки маркетинга).
Если вы добавляете в процессор систему маппинга регистров, то у вас усложнается стадия декодирования и, что ещё хуже, она начинает зависеть от режима, в котором находится процессор… в общем всё это далеко небесплатно.

Я про то, что если это уже сделано для половины регистров, то можно без дополнительных затрат расширить эту систему на все регистры.

А толку? WHQL — это просто некоторый набор тестов.

Они в том числе проверяют драйвера на использование «запрещённых приёмов».

Вы не поверите на что свособно воображение драйверописателей, у которых с одной стороны есть одна задача (пройти тексты), с другой другая (удовлетворить хотелки маркетинга).

devblogs.microsoft.com/oldnewthing/20040305-00/?p=40373
Я про то, что если это уже сделано для половины регистров, то можно без дополнительных затрат расширить эту систему на все регистры.
А… вы про это. Ну так её довольно давно Intel выпилил. Ещё при переходе с 8080 на 8086 в 1978м году. Про ARM не знаю — вроде документация от 1987го года (тут можно найти) говорит о том, что там действительно маппинг регистров имеется, но я не уверен, что в современных суперскалярах это всё ещё поддерживается…
Я про то, что если это уже сделано для половины регистров, то можно без дополнительных затрат расширить эту систему на все регистры.

У ARMv8 маппинг РОН убрали. Для поддержки AArch32 он всё ещё есть, аппаратно, но все теневые регистры отображены на 31 РОН. Некоторые современные ARM процессоры больше не поддерживают 32-битный режим.

> Время, занимаемое переключением регистров, не зависит от числа переключаемых регистров, потому что меняется только маппинг, самое содержимое регистров никуда не перекачивается.

Это будет означать, что количество архитектурных регистров будет умножено на количество уровней привилегий. Пусть их 4, как в типовых современных архитектурах — значит, ко всем номерам регистров добавится по 2 бита. Соответственно утолщение блока переименований… увеличение объёма пула реальных регистров на весь этот балласт… зачем?

Я боюсь, вы допускаете тут ошибку, полагая, что если говорят, что, например, SP каждый на свой уровень, то он реально хранится раздельно и подключается именно конкретный в каждый момент. Для более чем одного регистра это невыгодно, лучше копировать в момент переключения, а адресовать уже один регистр всегда.
И даже для комплекта для FIQ это скорее всего на момент перекидки использование прямой связи между тремя версиями одного регистра.
Я боюсь, вы допускаете тут ошибку, полагая, что если говорят, что, например, SP каждый на свой уровень, то он реально хранится раздельно и подключается именно конкретный в каждый момент.
В ранних процессорах так и было. Именно за счёт этого EXX, «меняющая» местами 4 регистровых пары в 8080 отрабатывает всего за 4 такта (вот тут есть немного на эту тему).

И даже для комплекта для FIQ это скорее всего на момент перекидки использование прямой связи между тремя версиями одного регистра.
В ARM2 точно не так. Они там за задержки очень переживали. Аж даже ради этого сотворили архитектуру где FIQ не берёт адрес инструкции откуда-то, а может прямо код иметь расположенный по адресу 1C: The FIQ routine might reside at 0000001CH onwards, and therebay void the need for (and execution time of) a branch instruction.

Но мне кажется из всех суперскаляров все эти извращения выпилили.
Именно за счёт этого EXX, «меняющая» местами 4 регистровых пары в 8080 отрабатывает всего за 4 такта

Z80.

Аж даже ради этого сотворили архитектуру где FIQ не берёт адрес инструкции откуда-то, а может прямо код иметь расположенный по адресу 1C:

Таблица векторов прерываний содержит не адрес, а инструкции, что часто использовалось в те времена (в вами же упомянутом 8080), плюс упрощало создание процессора.
Такая простая мелочь позволяла вдвое(?) сократить время реакции на прерывание.
Такая простая мелочь позволяла вдвое(?) сократить время реакции на прерывание.
Только в случае пустого обработчика. Что в реальном мире несколько… эээ… бессмысленно. Очередная победа маркетологов над здравым смыслом.
Время реакции не зависит от размера обработчика.

Очередная победа маркетологов

Тогда и слов-то таких не знали.
Думаю вам стоит вернуться в прошлое [шутка] и убедить Sophie Wilson потратить ещё немного транзисторов чтобы сделать как вам нравится.
Время реакции не зависит от размера обработчика.
То есть, я извиняюсь, как? Процессор у вас без исполнения какого-то кода что-то, внезапно, начинает делать?

Зависит, конечно.

Правда в этой истории есть и «условно счастливый» конец: вдрызг проиграв «гонку мегагерц» (во многом как раз из-за «клёвых находок» типа 26-битной адресации и FIQ) процессоры ARM оказались востребованы в бытовой электронике. И да — вот как раз там все эти штучки оказались «к месту».

Думаю вам стоит вернуться в прошлое [шутка] и убедить Sophie Wilson потратить ещё немного транзисторов чтобы сделать как вам нравится.
Даже если вы вернётесь в прошлое вы там никакой «Sophie Wilson» не найдёте. Вот с Роджером можно бы и поговорить — впрочем не слишком ясно о чём: ресурсов на то, чтобы создать что-то конкурентоспособное в сравнении хотя бы с 80486м у Acorn не было, а уж про Pentium (который появится через несколько лет) — тягаться и совсем бессмысленно.

Так что как раз FIQ можно и оставить (хотя на десктопе он не нужен), а вот как раз если что-то и столо бы предвидеть — так это то, что 64MiB окажется «небольшим объёмом» куда быстрее, чем казалось в 1985м…

Тогда и слов-то таких не знали.
Знали, конечно. И FIQ как раз под то, чтобы можно было заявить о том, как всё круто в пресс-релизах делался. А что он, потом, реально пригодился — но только уже после того, как Acorn обанкротился… это скорее счастливая случайность…
То есть, я извиняюсь, как?

Я подразумевал время до выполнения первой инструкции ISR.

вдрызг проиграв «гонку мегагерц» (во многом как раз из-за «клёвых находок» типа 26-битной адресации и FIQ)

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

а вот как раз если что-то и столо бы предвидеть — так это то, что 64MiB окажется «небольшим объёмом» куда быстрее

ARM был сделан чтобы решать проблемы «здесь и сейчас» а не когда-то в будущем.
Вы бы просто физически не могли подключить столько памяти к ARM2/3 и т.д.
68000 выдавал 24 бита адреса на шину по тем же причинам — экономия транзисторов.
Если соблюдать элементарную гигиену, программам решительно пофиг, сколько там бит выведено на шину адреса. Мои программы под 68к работают на любом другом.

Давайте лучше вспомним «640КБ хватит всем». A20 gate и прочие красоты х86.

Знали, конечно. И FIQ как раз под то, чтобы можно было заявить о том, как всё круто в пресс-релизах делался.
Пресс-релиз в студию! Иначе это враньё.
Продавался компьютер целиком, а не контроллер прерываний.
ISA спроектирована (тогда ещё Роджером) в одиночку для решения необходимого круга задач, а вовсе не для удовлетворения маркетологов. Цель была получить RISC-овый 32-битный «6502» с быстрой подсистемой памяти. Там всё продиктовано тем, чтобы максимально эффективно работать с памятью.
В итоге получился супероптимизированный процессор, легко опережающий 386, который имел в 10 раз больше транзисторов.
Блестящая инженерная работа!
Казалось бы, причём тут 26-битная адресация и мегагерцы. Интел выиграл гонку мегагерц за счёт своего производства. Acorn же не имел таких возможностей.
Acorn не имел таких возможностей, но партнёры ARM (DEC, Samsung и даже, внезапно, Intel) — несомненно имели.

Однако их процессоры, внезапно, не имели совместимости с 26-битным режимом, под который были заточены как RiscOS, так и куча софта.

Вы бы просто физически не могли подключить столько памяти к ARM2/3 и т.д.
Речь идёт не о подключении такого количетва памяти, а возможности написания софта, способного столько памяти использовать.

В ранние модели на 80386 тоже больше 16MiB не впихнуть, но Windows 3.1 может использова 512MiB… а программы для Windows 3.1 работают и в Windows 10, которая может и больше.

68000 выдавал 24 бита адреса на шину по тем же причинам — экономия транзисторов.
Однако же 68000 не пытался в те же 32 бита запихнуть ещё и статусные биты и потому апгрейд тех же Маков до 68020 прошёл без титанических усилий, требовавших переписать вообще всё.

Если соблюдать элементарную гигиену, программам решительно пофиг, сколько там бит выведено на шину адреса.
Как вы собрались «соблюдать элементарную гигиену», если на ARM2 флаги процессора засунуты в R15, слиты в единое целое с адресом инструкции и сохраняются просто командой STR как единое целое?

Давайте лучше вспомним «640КБ хватит всем». A20 gate и прочие красоты х86.
Ну там как раз всё понятно: чего вы вообще хотите от «заглушки для сокета»? 8086, как известно, был создан только для того, чтобы ну хоть что-то противопоставить Z80 и прочим всяким 68000, пока настоящий ответ готовился.

Что, собственно, и позволило Apple выжить и лет 10 просуществовать в статусе «премиальной платформы» — аж до 1995го года.

В итоге получился супероптимизированный процессор, легко опережающий 386, который имел в 10 раз больше транзисторов.
Блестящая инженерная работа!
Работа-то, может, и блестящая, но не без косяков. В частности FIQ (вот именно дизайн с размещением кода прямо по адресу 1C) и 26-битный режим — очевидные ошибки.

Хотя в целом — да, куда более качественная разработка, чем, скажем, 8086…
Прерывание — оно, знаете ли… прерывает. И потому должно работать быстро. Соответственно обычные регистры x64 сохраняет
А точно сохраняет? Вроде же выпилили на х64 аппаратное переключение задач, точно должно сохраняться CS,RIP,RFLAGS, а дальше ручками наверно (даже pushad выпилили).
Строго говоря вы правы: сохраняет действительно не процессор, а операционная система. Спасибо за поправку.

Но с практической точки зрения — это без разницы: важно что их кто-то да сохраняет. А вот x87/SSE регистры — не сохраняет никто… но это не значит, что в них нужно, тайком от мамы, залезать «через задний проход»… нет — это значит, что в них не нужно лазить. Совсем.
не сохраняет никто…
Хм…
Сохраняемые регистры вызываемого и вызывающего объектов
Регистры RAX, RCX, RDX, R8, R9, R10, R11, XMM0-5 и верхние части YMM0-15 и ZMM0-15 рассматриваются как изменяемые и должны считаться уничтоженными при вызовах функций (если иное не обеспечивается защитой при анализе, например при оптимизации всей программы). В AVX512VL регистры ZMM, YMM и XMM 16-31 являются изменяемыми.
Регистры RBX, RBP, RDI, RSI, RSP, R12, R13, R14, R15 и XMM6-15 считаются неизменяемыми и должны быть сохранены и восстановлены с помощью функции, которая их использует.
docs.microsoft.com/ru-ru/cpp/build/x64-calling-convention?view=vs-2019
Кто-то же этот бред ещё и плюсует… Вы не заметили, что мы тут, внезапно, обсуждаем обработчик прерываний в драйвере? А вовсе не вызов функций? Какое к нему имеет отношение процитированный вами документ?
Действительно, просматривая только свежие сообщения, выпал из контекста. Прошу прощения.
Не восстанавливать все пользовательские регистры опасно утечкой данных, потому что в стандартном соглашении вызова те регистры, что args + result + scratch, никто не чистит, а в callee-saved, наоборот, может что-то лишнее притечь ядру… опасно.
Абсолютно безопасно. Задаёте опцию -mno-sse — и нет проблем, никаких утечек.
> Абсолютно безопасно. Задаёте опцию -mno-sse — и нет проблем, никаких утечек.

Общие регистры вы принципиально игнорируете? В них обычно сильно более интересные данные, чем в SSE :)

В текущем коде их, насколько вижу, восстанавливают равным значениям на входе.
В текущем коде их, насколько вижу, восстанавливают равным значениям на входе.
Совершенно верно — а поскольку их восстанавливают, то утечек быть не должно (если забыть про Spectre).

А вот x87 и SSE регистры обработчики прерываний просто не должны трогать — и всё.

Даже пресловутый драйвер RAID в Linux не использует SSE регистры внутри обработчика прерываний.
> FXSAVE, которая, внезапно, появлась аж в Pentium II (то есть до изобретения SSE даже).

Ага, вот где меня заглючило — решил по кривым описаниям, что она появилась позже SSE. Спасибо, тогда полностью согласен.
Что мешало сделать SSE-иструкции просто копией AVX-инструкций с двумя операндами?
Простите что? SSE появился в 1999г, AVX в 2011.
Простите. Голова. Мозги. Думать.

На процессоре с 256-битными регистрами, внезапно, AVX есть всегда есть. По определению. И если бы SSE инструкции были бы переопределены внутри процессора как 128-битные инструкции с двумя операндами — совместимость не пострадала бы ни на йоту.

Вместо этого… имеем вот весь этот геморой.

А зачем бить себя лопатой по голове использовать AVX и SSE одновременно?
Ну, например, чтобы не тащить с собой две копии всех функций: для «старых» процессоров и для «новых».
Простите. Голова. Мозги. Думать.

Какой вы нервный, однако.
Я не счёл нужным ставить табличку [сарказм]. Похоже что зря.

И если бы SSE инструкции были бы переопределены внутри процессора как 128-битные инструкции с двумя операндами — совместимость не пострадала бы ни на йоту.

AVX-128 и SSE это разные наборы команд. Да, SSE могли бы чистить старшую половину (например, в зависимости от режима работы процессора), но так не сделали, и с этим нужно жить.

например, чтобы не тащить с собой две копии всех функций: для «старых» процессоров и для «новых».
И ничего бы не изменилось — AVX-128 более совершенный набор чем SSE.
На числа невозможно заявить интеллектуальную собственность.
Грубо говоря, ничто не мешает конкуренту Intel сделать чип с поддержкой SSE и продавать его под названием «SSE-256», даже если Intel под «SSE-256» понимает нечто совсем иное.

Поэтому Pentium вместо i586, поэтому AArch64 вместо ARM-64, и т.д.
> сведение en.wikipedia.org/wiki/X86_instruction_listings в одну табличку:

Теперь хорошо бы сие изображение в ту же википедию запостить.
Это канал про анимэ? Раз уж тут пошла такая пьянка, подскажите пожалуйста курс/книгу который бы последовательно и подробно разложил по полочкам всю следующую информацию: виды плат(fpga, asic, микропроцессоры), архитектуры(фон неймана, dataflow), наборы команд и их архитектуры(risc/cisc и тд) и микроархитектуры(условно чем отличается zen от conroe). Хочется как-то разбить по категориям эту кашу в голове.
UFO just landed and posted this here
Оказывается, закон Мура был о числе инструкций)
Sign up to leave a comment.

Articles