Галопом по вычислительным Европам. Часть 1. Что такое процессор.
Что еще можно предпринять для увеличения количества обрабатываемой за единицу времени информации? Один из этих путей мы уже видели — это увеличение размера операнда с 8 до уже 64 разрядов. Можно больше? В принципе да, почему нет? Другое дело, что больше 64 разрядов пока не очень нужно, кроме как в некоторых алгоритмах. Заметили слово «некоторых»? Идея в том, чтобы реализовать специальные команды, позволяющие в некоторых алгоритмах (особенно, связанных с обработкой потоковых данных, в первую очередь аудио- и видео) работать с операндами еще большей длины.
Первая реализация на потребительском рынке появилась еще в эпоху 32-разрядных процессоров — расширение MMX позволяло использовать 64-разрядные операнды (как сами по себе, так и в виде наборов из нескольких операндах меньшей разрядности — в виде «упаковок» из 2х32, 4х16 или даже 8х8, т. е., в одном такте исполнялось восемь операций над 8-разрядными данными), резко увеличивая скорость обработки аудио-/видеоинформации.
Эффект в скомпилированных под MMX приложениях получился большим. Разница в производительности при равной частоте могла получиться в полтора-два раза (в некоторых случаях). Всем понравилось, и в следующих SIMD расширениях SSE/AVX длина операндов достигла сначала 128 (меньше для 64-разрядного процессора уже бессмысленно), а потом и 256 бит.
Поколения SIMD-расширений Intel
Intel попыталась запустить и 512-разрядное SIMD расширение, AVX-512 но впоследствии на десктопах его заблокировали по ряду причин, оставив пока на серверах. Их же конкуренты из AMD только недавно запустили AVX-512 в десктопных 5 nm Ryzen 7000, обещая поддержку на серверах только в будущем. В общем, необходимость работы с 512-разрядными операндами пока неочевидна. Ускорение, как видно из примера ниже, в отдельных случаях, может быть большим, но область применимости AVX-512 пока невелика.
Как использование SIMD сказалось на производительности на этих определенных операциях? Очень хорошо сказалось. Вот, к примеру, самый драматичный из известных мне случаев: качественно оптимизированный под использование SIMD эмулятор Play Station 3 под названием RPCS3. Посмотрите, как выглядит на нем приставочная игра «God of War 3» в зависимости от доступности версий SSE и AVX.
Кроме того, SIMD расширения очень востребованы в алгоритмах искусственного интеллекта, набирающих популярность в последние годы. Ускорение относительно «чистого кода», не использующего такие расширения, получается в несколько раз.
Да, я в курсе, что для машинного обучения гораздо более эффективны высокопараллельные вычисления, реализуемые на специальных ускорителях, архитектурно выросших из видеоадаптеров. Об этом мы поговорим ниже.
Но обучение обучением, а когда дело доходит до принятия решений на основании этого обучения, то приходится обращаться к классическим головоломным ветвящимся алгоритмам, которые на ускорителях чувствуют себя не очень хорошо, им подавай универсальные вычислительные ядра процессоров.
Какие еще специфические операции нуждаются в ускорении? Конечно, шифрование. Постоянно и повсеместно используемые алгоритмы просто напрашивались на аппаратную реализацию. Исторически первыми появились специализированные «ускорители шифрования», которые вставлялись в серверы для разгрузки их процессоров (согласитесь: web-сервер с тысячами одновременных клиентов не должен тратить ¾ процессорного времени на шифрование/дешифрование соединений?) а потом это ускорение просто внесли в сами процессоры, title="Интересно, что сегодня шифрование сетевых соединений снова выносится из процессора, в интеллектуальные сетевые адаптеры, оснащенные собственными процессорами. Об этом подробнее расскажу ниже, чтобы не умножать сущности. Поначалу в нуждах криптографии активно использовали SIMD расширения (вот эти MMX и SSE, да), а потом...
Потом, после принятия в качестве стандарта шифрования алгоритма AES-256, сначала компания Intel, а потом и AMD ввели в процессоры специальные инструкции, ускоряющие шифрование по этому алгоритму. Почему решили ускорять именно AES-256, почему раньше никого не ускоряли и почему не его конкурентов? Потому что корректно реализованный 14-раундовый AES-256 это навсегда. Доказано, что этот алгоритм не взламывается «в лоб», путем перебора, никакими вычислительными мощностями, ни существующими сегодня, ни даже теми, которые могут быть созданы в будущем. Все существующие в теории атаки на AES-256 используют различные боковые утечки, практическое использование которых так скажу: маловероятно вне лаборатории. Как правило, все эти сценарии требуют физического доступа к компьютеру в процессе шифрования или внедрения туда специального ПО. «Холодный» массив, зашифрованный на чистой (без шпионского ПО) аппаратуре атакам не поддается.
Поэтому разработчики и занялись реализацией специальной подсистемы команд для криптоалгоритмов именно исходя из требований AES-256.
Насколько новые команды ускоряют шифрование? Смотрите, никаких секретов. Вот скриншот теста производительности шифрования из утилиты VeraCrypt, кроссплатформенной системы шифрования данных. Все вполне очевидно: AES-NI очень полезное расширение, увеличивающее скорость шифрования по алгоритму AES-256 в семь раз даже на моем очень старом (2012 года) процессоре (при одновременном сокращении энергопотребления).
На новых процессорах ускорение получается, иногда, в десять и более раз.
Какое вам дело до шифрования, если вы простой домашний пользователь? Посмотрите на «замочек» в заголовке адреса сайта. Это означает, что ваше общение с сервером зашифровано. Вот это шифрование и ускоряется при использовании AES-NI. Грубо говоря, процессор с аппаратным шифрованием тратит на эти процедуры меньше энергии, а стало быть — ваш любимый ноутбук (или телефон) проработают при работе в сети от батарейки заметно дольше, чем без этих команд.
Вопрос же об использовании других способов шифрования, не поддерживаемых аппаратно, мы обсудим в приложениях, читать которые не обязательно. Но полезно.
Пути повышения IPC: динамическая оптимизация против статической
Давайте подумаем, что можно предпринять, чтобы максимально полно использовать все то, что мы придумали в процессор засунуть, и чтобы все эти несколько параллельных специализированных конвейеризованных исполнительных устройств у нас работали, а не просто там были. Есть, собственно, два пути: разбираться с кодом по мере поступления, динамически переопределяя порядок исполнения команд — или постараться оптимизировать его на этапе компиляции, не усложняя процессор. Третий же путь сочетает оба подхода — динамически разбираться с насколько возможно оптимизированным кодом.
Суперскалярность, динамическая оптимизация исполнения и одновременная многопоточность.
Еще до всякой многопоточности и многоядерности у разработчиков возникла здравая идея нарушения последовательности исполнения команд. В самом деле: если команда «N+1» не зависит от результатов исполнения команды «N», то почему бы не выполнить их одновременно и даже в обратной последовательности (если, к примеру, данные уже под рукой)? В мире PC первым таким процессором стал знаменитый Pentium. Помимо основного целочисленного исполнительного конвейера «U» процессор получил несколько упрощенный конвейер «V», на котором могли исполняться некоторые инструкции. Почему только некоторые? Анализ программного кода показал, что распараллелить исполнения чаще всего получается именно с этими инструкциями, а стало быть, нет смысла усложнять дополнительный конвейер поддержкой всех команд. В итоге, процессор получил возможность исполнять больше одной команды за такт, стал суперскалярным. Дальше — больше.
Собственно, откуда появилась идея одновременной многопоточности (SMT) в процессорах, т. е., исполнения более чем одного потока команд одним набором исполнительных устройств? С ростом частот и количества исполнительных устройств в ядре процессора (в среднем, современные процессоры имеют по четыре целочисленных исполнительных устройства, от двух до четырех устройств с плавающей точкой и от двух до четырех устройств загрузки-выгрузки данных) появилась неприятная ситуация: на этапе исполнения команды часто оказывалось, что текущей командой задействована только небольшая часть собственно «считающих узлов», а остальные просто греют Вселенную, делать им нечего, и даже суперскалярность внутри одного потока команд (описанная выше) уже не спасает, потому что независимых команд не хватает для заполнения всех исполнительных устройств. Поэтому возникла резонная мысль: а почему бы не заслать в свободные исполнительные устройства команды вообще из другого потока (другой программы), оснастив процессоры, разумеется, дополнительным набором регистров и контроллером прерываний? Ну и заслали — получилось очень неплохо. Суммарная производительность процессоров с включенной технологией ...да, правильно, на х86 рынке она объявилась как Hyper Threading — оказалась существенно выше за счет заполнения свободных временных и ресурсных интервалов.
Правда, оборотной стороной монеты стала несколько сниженная скорость исполнения однопоточной «главной задачи», если таковая есть, потому что иногда ей приходилось подождать, а что делать? За увеличение общей производительности примерно на 20-40% (не вдвое, конечно, чудес не бывает) заплатить увеличением количества транзисторов всего на 5-7% очень и очень «вкусно».
Однако, что называется, есть нюанс: увеличение КПД означает и увеличение нагрева кристалла. Поэтому многопоточность не применяется в low-end процессорах, где уменьшение пикового нагрева критически важно, важнее производительности и экономии площади кристалла. Иногда даже лучше иметь четыре ядра без многопоточности, чем два многопоточных — при равной общей производительности их охлаждать проще.
Хорошо, к двум потокам на ядро мы уже привыкли, а как насчет восьми? Компания SUN Microsystems чуть задержалась с многопоточностью, но в 2005 году выстрелила процессором UltraSPARC T1, который в младшей версии имел четыре ядра, исполнявших по четыре потока одновременно, а в старшей — имел восемь таких же четырехпоточных ядер. Спустя два года та же компания SUN выкатила процессор UltraSPARC T2 с «колесной формулой» 8x8. В смысле, восемь потоков на ядро и 64 потока на сокет. Самый быстрый (на сокет) процессор на тот момент. Разумеется, 8 потоков на ядро это предельный случай, годный только для серверных систем с достаточно большим (см. в следующей главе) количеством задач. Потому что ускорение тут совсем не в восемь раз — по сравнению с временной многопоточностью прирост производительности выходит, в лучшем случае, в два раза, и только в очень редких ситуациях больше. Тут речь идет именно об эффективности использования транзисторов. Столько же потоков на ядро и у нынешних серверных процессоров компании IBM, а текущая топовая реализация, двухчиповый CISC-процессор для мэйнфреймов IBM шестнадцатого поколения IBM Telum имеет 2х8=16 ядер по 8 потоков на ядро, т. е., 128 потоков на сокет при крайне интересной общей организации системы, обеспечивающей кормление всех этих потоков исполнения данными в достаточном темпе.
Примерно так же обстоят дела и у RISC-процессоров IBM серии POWER. Там SMT впервые появилась в 2004 году у процессора POWER 5 (в виде двух потоков на ядро), а сегодняшний POWER 10 уже выпускается в двух вариантах — 15-ядерный с восемью потоками (SMT8) на ядро для систем с «высоким КПД» и 30-ядерный с четырьмя потоками (SMT4) на ядро для высоконагруженных систем.
Разумеется, это требует невероятной и далеко выходящей за пределы данной статьи сложности систем предсказания ветвлений и предисполнительного анализа поступающего в процессор кода для переупорядочивания исполнения. В современных процессорах эти планировщики занимают заметную часть кристалла, а сложность их схемотехники давно является узким местом с точки зрения повышения частот — но зато такая оптимизация работает всегда, поскольку обрабатывается тот код и в той ситуации, которая складывается на данный момент.
Ссылки на все 10 статей цикла «Галопом по вычислительным Европам»:
Галопом по вычислительным Европам. Часть 1. Что такое процессор.
Галопом по вычислительным Европам. Часть 2. Пути повышения IPC.
Галопом по вычислительным Европам. Часть 3. Оптимизация.
Галопом по вычислительным Европам. Часть 4. Как накормить процессор.
Галопом по вычислительным Европам. Часть 5. Память.
Галопом по вычислительным Европам. Часть 6. Спецпроцессоры.
Галопом по вычислительным Европам. Часть 7. Ввод-вывод.
Галопом по вычислительным Европам. Часть 8. Хранение данных.
Галопом по вычислительным Европам. Часть 9. Параллельные миры и техпроцессы.
Галопом по вычислительным Европам. Часть 10. Китайский путь и персональная безопасность.