Comments 48
спасибо за статью. вопрос знатокам: а возможно ли спекулятивное выполнение без SMT? по данным из статьи кажется, что да. и тогда не очень понятно почему хотят избавиться от SMT, ведь по факту уязвимость со спекулятивным выполнением, а не с гипертрединг. или не так понял?
а возможно ли спекулятивное выполнение без SMT?
Да
SMT недостаточно эффективен. В M2, M3 там четыре потока в каждом ядре — 4 микроинструкции из разных потоков все в один цикл идут.
А на Intel 2 потока. Это такое...
Микроинструкции не имеют отношения к потокам исполнения, а только о числу execution-units (в современных процессорах перед каждым из них своя очередь ожидающих инструкций).
И да не слышал, чтобы Apple (если M2 M3 - это про Apple) использовали SMT (нам cache throttling между ядрами может начаться - в статье об этом неприлично вскользь сказано) - сейчас подход P/E ядра.
Это как так он недостаточно эффективен? Он способен и зачастую ведет к улучшению производительности. Меня, как разработчика, интересует компиляция. Компилируем. Включаем SMT и компилируем. Видим как минимум +20% улучшение - и забываем этот бред вообще. Есть задачи где он будет во вред, ну так SMT4 тогда будет еще хуже...
Какие 20%? Каждое логическое ядро это физическое ядро у Intel, если код написан правильно. Имеется в виду, что если 60%, 80%, то уже плохо. Ведь должно быть 95% или 90% по факту дизайна.
Ведь должно быть 95% или 90%
Если задача полностью распараллеливается.
А в компиляции может быть полно однопоточных мест.
Ну, процессор бесконечный цикл вообще может выполнять очень и очень быстро, а че ему? Все в кеше, считать не надо, в память ходить не надо, с внешним миром общаться не надо - сиди и считай тока метрики/такты, только пользы ноль, зато быстро. ))
Мои 20% это то что я видел на разных процессорах разных поколений, да специфично для задачи, но это все равно вполне себе эффективно. В моем случае каждый 1% транслируется на минуту реального времени. Что бы получить больше и продолжить вертикально масштабироваться - это вообще в другой сегмент железа. Так что очень даже эффективно. Если бы был выбор между 16 SMT ядрами и 20-тью мощными не-SMT ядрами - можно было бы что-то там думать об эффективности, а так оно есть и вполне применимо.
Каждое логическое ядро это физическое ядро у Intel
Это как раз от HT зависит - его включение не меняет число физических ядер, а логических становится в два раза больше.
Каждое логическое ядро это физическое ядро у Intel,
Нет, с SMT вы получаете в два раза больше логических ядер, чем есть физических.
Нет. Логические ядра и есть физические если всё написано правильно.
Это зависит от процессора и конфигурации в BIOS, а не от кода. Или ваша терминология не совпадает с интеловской )
Intel на физ уровне удваивает компоненты, или как иначе обеспечить примерно в два раза большую скорость?
Функция отключения SMT не является адекватной настройкой.
А расскажите-ка, чем включение SMT отличается от простого удвоения числа ядер?
Ничем. Или вы думаете, что ядра должно быть видно на снимках архитектуры процессора? Или вы думаете, что в процессоре за 1 цикл исполняется одна команда? Нет, может быть 5 (суперскаляр, вот 4 FLOPs per cycle https://stackoverflow.com/questions/8389648/how-do-i-achieve-the-theoretical-maximum-of-4-flops-per-cycle), может за 30 циклов 1 деление, как с DIVQ/IDIV. Операция целочисленного деления использовала FP делитель до Ice Lake. В Ice Lake они создали настоящий целочисленный делитель, 30 циклов ушли в прошлое. В ARM Apple M1 64 битное деление занимает 2 такта, причём в любом случае, что бы ты не делил. https://ridiculousfish.com/blog/posts/benchmarking-libdivide-m1-avx512.html
И вообще каждая команда может быть больше однлй микроинструкции RISC на Bigcore. Микроинструкции описаны тут, если интересно. https://github.com/chip-red-pill/uCodeDisasm/blob/master/misc/bigcore_opcodes.txt
Суперскалярность у Интела начиная с Pentium Pro, к многоядерности и SMT это несколько ортогонально (хотя SMT позволяет эту самую суперскалярность более эффективно использовать).
Суперскалярность у Интела начиная с Pentium Pro
Самая тупая - начиная с Pentium 1 - когда команды по очереди идут в два соседних блока, U и V. Код явно оптимизированный под P1 в этом смысле выглядит прикольненько: на длинных кусках можно чётко видеть, как нечётные команды исполняют одно, а чётные - другое, пока есть возможность им не общаться.
Начиная с PPro, да, они сумели реализовать полноценный out-of-order с переименованием регистров и точными исключениями, и на этом взлетели дальше. (И казалось бы, при чём тут госгрант на ASCI Red?)
SMT может удвоит производительность разве что на синтетическом latency bound тесте, в реальной жизни 20-30% уже хорошо. Как оно вообще увеличивает производительность на том же числе физических ядер - в статье достаточно понятно написано. Более детально (но применительно к GPU, где SMT ещё важнее) - в кандитатской Волкова.
30% это без оптимизации... цифра от intel. По факту вот https://superuser.com/a/279803/1033761
4.16 секунды на одном ядре, а на 8 с SMT 0.35 секунды. Т.е. в 13 раз.
И там ещё в Linux EEVDF завезли.
В 13 раз на 16 логических ядрах, и автор чётко пишет, что алгоритм упирается в memory latency - идеальный случай для демонстрации эффективности SMT
In this particular case it’s memory load; the first two algorithms need more memory for the calculation, and are constrained by the performance of the main memory bus. This means that while one hardware thread is waiting for memory, the other can continue execution; a prime use-case for hardware threads.
На 8 ядрах достигается ускорение в 7 раз. Так что если на 16 в 13 раз это почти в два раза быстрее.
Да, но это идеальный случай, и я как раз выделил жирным чёткое объяснение, как такое ускорение достигается без дополнительных исполнительных устройств. В реальном большом приложении такого эффекта не будет, а в оптимизированном коде, который и так использует вычислительные ресурсы по максимуму (скажем, умножение плотных матриц), может быть только замедление из-за дополнительных накладных расходов - поэтому на кластерах, используемых в HPC, SMT обычно отключают.
Что Вы называете физическим ядром?
У POWER-процев по 8 потоков на ядро может быть.
Если некоторые уязвимости спекулятивного выполнения связаны с гипертредингом, а некоторые -- нет, то можно, убрав гипертрединг, избавиться хотя бы от первых. Логично ведь?
Может, в следующих версиях процессоров собираются не просто удалить сабж, а заменить его новой логикой, перераспределяющей свободные ресурсы процессора между ядрами, при этом формально не являющейся гипертредингом.
Уязвимость со спекулятивным исполнением (к стати нигде даже мельком не видел, чтобы такие атаки реально работали и применялись, в отличии от дыр в ПО).
SMT - лишь один из способов заэксплуатировать (через общий L1D-Cache) эту уязвимость.
Как по мне - так проблема в кэшах (о том, что два потока исполнения начинают конкурировать за L1D-Cache процессора вытесняя данные друг-друга написано в конце и очень вскользь).
Я бы связал это с тем, что сейчас более модный (и возможно более правильный) подход - с P/E ядрами.
Если вам нужна фоновая работа (как правило не требующая высокой производительности) это планируют на [E]fficient - ядро). Для задач с высокой производительностью вам нужно P[ower] ядро. И за L1D-Cache они между собой не конкурируют.
П.С.
Другой вопрос - как в data-центрах продавать процессорное время с SMT-процами.
Linux же считает сколько задача была спланирована на ядре.
Сколько реально она выполнялась - зависит от параллельной нагрузки. Клиенты могут быть ОЧЕНЬ сильно недовольны.
не очень понятно почему хотят избавиться от SMT
Если речь о том, что Intel хочет отказаться от гиперпоточности (а точнее новое поколение процессоров у них уже вышло или вот вот скоро выйдет без гиперпоточности, так как анонс был на презентациях). Так это потому что мелкие ядра, да и вообще, более многоядерная архитектура, гораздо эффективнее. Да, гиперпоточность может экономить транзисторный бюджет. Однако, на практике, люди отрубают гиперпотчоность и в некоторых задачах производительность даже растёт (разумеется в теории не должна расти, но отладить код, чтобы всё было идеально очень сложно). А там, где растёт - там энергопотребление непозволительно возрастает, что приводит к перегреву и так переразогнанного процессора, даже под хорошей системой охлаждения, поэтому привет троттлинг и деградация.
Всё вышесказанное касается именно процессоров "для домашних систем". У северных может быть всё по другому, там, как миниум, площадь поверхности в разы больше (что сильно облегчает охлаждение), и задачи ставятся несколько иного характера.
Избавиться от НТ могут в том числе и потому что придумали что-то новое, с кодовым названием Rentable Units. Патенту уже год и предполагается что 16+ поколения уже будет такими.
Там даже описательная часть написана полуюридическим языком - пролистав, так и не смог понять, сколько логических ядер предлагается показывать операционке (для latency hiding через SMT принципиально, чтобы их было больше, чем физических ядер).
Произведя некоторый поиск информации я так понял, что кроме патента пока ничего и не известно про эту фичу извне.
Пока остается только ждать когда все это появится в свежем релизе Intel® 64 and IA-32 Architectures Software Developer Manuals
Кажется, что перевод этой статьи - пустая трата сил.
(Во-вторых: есть сильные подозрения, что в статье - в целях сокрытия деталей от конкурентов - в некоторых важных деталях прямо наврано).
Главная же проблема - чтобы полезно для себя читать эту статью, требуются сначала предварительные материалы минимум на 2 таких статью (это если прям супер-ускоренно).
Хотябы самая верхнеуровневая проблематика:
- что имеем
- какие проблемы есть
- какие проблемы хотим решить
- способ решения.
На мой взгляд любой перевод это хорошо. Как минимум, делает интернет доступнее, помогает людям разбираться в различных вопросах, кто на статью наберёт. Есть только одно исключение - первод должен быть выполнен качественно и не содержать технических ошибок, которые могут способствовать некорретному пониманию рассматриваемого вопроса.
А что такое декодирование команды и почему это затратная операция? Вот возьмём ассемблер pdp11. Получаем слово, 16 бит. Несколько схем с и или и not и получим выводы принимающие 1 для нужной команды. Обычно надо анализировать не все биты
У интела переменная длина команд (кажется, от 1 до 15 байт). Чтобы знать, где начинается каждая следующая команда, надо хоть как-то минимально декодировать предыдущую.
Насколько я знаю (не мой профиль, могу ошибаться), для ускорения декодеры работают параллельно и в предположении что каждый байт - начало валидной команды, пытаются декодировать всеми возможными способами, а потом лишнее откидывается.
Каждая макроинструкция может быть несколько микроинструкций. Это видно по отладочным счетчикам. Таким образом можно перебором определить все команды, что исполняют что-то на проце в микроинструкциях, даже если CPU говорит, что ничего не произошло.
https://blog.can.ac/2021/03/22/speculating-x86-64-isa-with-one-weird-trick/
А вот и все инструкции https://haruspex.can.ac/
Блин, вроде интересно, но картинки как будто совершенно не связаны с текстом. Сколько не разглядывал, так и не понял соотношения с написанным.
По сути идея - два фронтэнда (fetch \ decode \ issue) : один бэкэнд.
Это максимально просто и понятно на уровне идеи.
А вот чтобы понять дальше - нужно объяснять про современные процессорные архитектурные, что совершенно упущено в статье.
Бэкэнд, в современных ОоО-процессорах даже близко не похож на то, как на картинке, а представляет собой развитие вот этого подхода:
https://translated.turbopages.org/proxy_u/en-ru.ru.5f38ea06-66c539fc-e3c02850-74722d776562/https/en.wikipedia.org/wiki/Tomasulo%27s_algorithm
Если совсем кратко:
- в кольцевой буфер кладём инструкции
- регистры разыменовываем с дополнительным уровнем косности (т.е. не AX, а reg-112)
- когда все регистры инструкции готовы - инструкцию на исполнение
- когда инструкция исполнилась - оповещаем, что её регистр (переименованный) готов на общую шину
- фиксируем все исполненные подряд идущие инструкции из хвоста очереди (это называется retire).
В самом начале статьи читаем:
Кроме того, современные процессоры являются суперскалярными, то есть вместо отправки в каждом такте одной команды они могут отправлять несколько. Например, процессоры Intel Core i7 могут отправлять в каждом такте по четыре команды. Этот параметр называется шириной конвейера (issue width) процессора.
Что, прям любой i7 любого поколения? А любой , i5 нет. Серьёзно? Больше похоже на скрытую рекламу Core i7. Ну или проблема перевода имелся ввиду i7 актуальный момент написания статьи - может тогда он вообще был только один (но какого года статья мы не знаем, а в 2002 вообще не было i7)? Не помешало хотя бы примечание переводчика. А то возникает ощущение жуткой древности и грубых общений - сразу расхотелось читать.
Статья крайне странная. Не сказано прямо, что SMT вместо полноценных раздельных ядер сделана, чтобы загружать недозагруженные иначе общие блоки ядра, в первую очередь АЛУ, у которого множество блоков, но обычным кодом они используются по минимуму. Нет даже намёка на то, что SMT полезен поэтому в ряде специфических подходов: например, на 0-ю нитку каждого ядра ставим код с тяжёлым использованием SIMD, а на 1-ю - без SIMD (хорошо для игр) - хотя бы этот простой пример надо было привести. Или влияние скорости памяти, как в соседних комментариях. Непонятно, чем horizontal waste отличается от vertical, если там тоже нужно найти "независимую" инструкцию. Не сказано, чем отличается "код общего назначения", чем и как измерять возможную полезность от SMT или её отсутствие.
Вместо этого куча воды с повторением одного и того же, занимающая ¾ статьи. Похоже, автор гнался просто за количеством слов с нулевым пониманием темы, а переводчик решил ему следовать в этой эстафете.
трейс-кеш это что-то ужасно старое. заменили на микроопс кеш и он делится ровно пополам на интеле, что описано в статье про уязвимость (https://ieeexplore.ieee.org/document/9499837).
Мне кажется, проблема smt в том что его серьезно никто за 20 лет не стал воспринимать. ("Мы запустили программу name и она быстрее/медленнее на x% с включенным/выключенным SMT"). Это как эффективность SSE оценивать по фпс в doom. Кооперативные потоки это отлично (например про умножение матриц я специально выяснял), но как гарантировать их кооперативность я не нашел. Как заставить компилятор конкретные функции оптимизировать под порезанное ядро тоже не нашел (да и всю прогу целиком). Не исключаю что плохо искал, но как минимум хорошо спрятано. При этом всякие бигдаты с рандомным доступом на вид должны разгоняться очень хорошо (пример кстати хороший)
Два потока, одно ядро: как устроена одновременная многопоточность