Comments
Удивительная статья, рациональный и охватывающий много факторов взгляд на проблему производительности. Спасибо за перевод!
Первые IBM — PC были на базе 8088, а не на 8086, насколько мне известно…
IBM выбирала между 68000 и 8086, но тут появился 8088 с внешней 8 битной шиной, что позволило значительно удешевить материнскую плату и IBM остановила свой выбор на 8088.
В части открытых архитектур (а эта статья по большому счету — реклама RISC-V) интересно, как будут сосуществовать RISC-V и MIPS, который, став открытым, сразу же предлагает доступ к hi-end ядрам, а также как они вместе или по отдельности будут пытаться украсть у ARM полноценный кусок пирога, а не крошки.

P.S. Ну и VLIW красиво походя пнули. Что ответят корифеям фанаты Эльбруса?
Что можно ответить? «Компиляторы невозможно создать» оказалось просто «мы не смогли создать». Что касается эффективности, то все очень сильно зависит от решаемых задач.
Были архитектуры очень экзотические и очень эффективные, но для ограниченного круга задач. Например Сетунь с троичной системой. Или баллистический вычислитель с представлением чисел в виде набора остатков от деления на простые числа. Все это было оттеснено массовостью универсальных процессоров.
Если бы понятие серверов данных, файлов и приложений появились до создания архитектуры массовых процессоров, под них были бы созданы различные высокоэффективные архитектуры, со своими системами команд.
Про одного Мура упомянуто в статье, а про другого и его MISC процессоры забыли. :)
Статья (на английском) «Chuck Moore: Part 2: From Space to GreenArrays»
www.cpushack.com/2013/03/02/chuck-moore-part-2-from-space-to-greenarrays

P.S. Неплохо, для ретро-сравнения и данные этой статьи включить.

А, вот что делалось в те времена и в СССР
«Стековые процессоры, или новое — это хорошо забытое новое»
www.kit-e.ru/articles/cpu/2003_09_98.php
www.kit-e.ru/articles/elcomp/2004_1_102.php (продолжение)
www.kit-e.ru/articles/elcomp/2004_2_130.php (заключение)

Чужие: странная архитектура инопланетных компьютеров
www.ferra.ru/review/techlife/philae-computer.htm

Не всем процессорам и их архитектурам посчастливилось найти своего «массового» потребителя.

F21 in a Mouse www.ultratechnology.com/scope.htm
UFO landed and left these words here
Чтобы оценить важность этого наблюдения, рассмотрим рисунок 5. Он показывает, насколько быстрее приложение работает с 64 ядрами по сравнению с одним ядром, предположив разную долю последовательных вычислений, когда активен только один процессор. Например, если 1% времени вычисление выполняется последовательно, то преимущество 64-процессорной конфигурации составляет всего 35%.

Не 35% а в 35 раз…
«Рынок в конечном итоге определяет победителя в споре архитектур» — к сожалению, это не так. На рынке, особенно связаном с крупными капиталовложениями, очень сильны положительные обратные связи. Преимущество получает тот, кто раньше вышел на рынок, даже если его решения технически хуже.
Так определяется рынком не технически лучшая архитектура, а наиболее коммерчески успешная. Так что все сходится.
рынок выбрал сделанный впопыпах 8086, а не «помазанника» iAPX-432
Такие «истории успеха» весьма показательны.
Правы «зелёные»: «подводной лодке» под названием «планета Земля» дикий размах «экономической самоуправляемости» просто «не по карману».

( Кстати, на вышеупомянутом CPU для ADA вирусы/эксплоиты просто невозможны )
в 62 806 раз быстрее, чем первоначальная Python-версия

Прям обидно, как питон подставили. Для перемножения матриц есть специальные алгоритмы. А они, подозреваю, цикл со сложностью О(n^3) сделали. Разумеется, эти алгоритмы реализованы в виде библиотек на си и фартране, но всё же можно не терять ни капли эффективности работы программиста и при этом получать все плюшки скорости.

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

Нужна не ассемблерная вставка, а, например, алгоритм Штрассена. Несомненно, на питоне тот же самый алгоритм будет медленнее, но на практике вы никогда не будете его реализовывать, независимо от того, на питоне вы пишите или на си. То есть независимо от того, какой язык вы выберете, скорость будет одинакова.


Кстати, если вы на си будете перемножать матрицы наивным циклом, это будет медленнее, чем использование питона + numpy.


То есть на практике, если вы захотите получить ускорение и для этого возьмёте си — вы ускорения не получите. Именно это я и имел ввиду, говоря "подставили". Типа, если нужна скорость, берите си. Но для данной задачи (перемножение матриц) это неверно.

Мухи отдельно, котлеты отдельно.
Есть правильный выбор алгоритма. Он от языка не зависит и тут не рассматривается.
Рассматривается только насколько эффективно команды языка высокого уровня превращаются в машинный код. Это зависит от качества компилятора. И от возможностей «понять» смысл кода. Если отказаться от понятия массива в пользу более общей абстракции списка, то доступ к конкретному элементу возможен только стандартным способом и прямая адресация памяти невозможна. Значит компилятор не может заменить обращение к функции получения элемента по номеру на инкремент адреса. Вот и разница больше, чем на порядок.

насколько эффективно команды языка высокого уровня превращаются в машинный код

Если 99% времени выполнения занимает вызов библиотеки на фартране, то разница в эффективности трансляции в машинный код между питоном и си несущественна.


Обратите внимание, я не говорю, что нет разницы в скорости между питоном и си — она есть и всем очевидна. Но если кто-то выберет си ради задач по матрицам ради упомянутого в статье прироста в тысячи раз — его ждёт разочарование. Если только он не будет специально писать код как в статье, конечно.

Если 99% кода написано на одном языке, то разницы не будет.
Разговор не о случае, когда есть готовые библиотеки и мы их только вызываем.
Приходилось мне писать такие программы, которые были только специализированной под терминологию заказчика оберткой для стандартной библиотеки. Их действительно все равно на чем писать.
Разница возникает при реализации вычислительных алгоритмов. И тут очень простая зависимость: чем выше уровень абстракции, тем хуже будет результат.
Если язык не пускает тебя к железу, то и оптимизировать не выйдет.

Дональд Кнут: «Предполагалось, что подход Itanium… будет потрясающим — пока не оказалось, что желаемые компиляторы в принципе невозможно написать».
А почему? В чём оказалась проблема?
Боюсь сильно упростить, но попробую сформулировать: вроде как они не смогли выжать должного параллелизма уровня команд, т.е. написать такие компиляторы, которые смогут заполнить слоты команд EPIC/VLIW процессора эффективно с учётом ветвлений, кэшей и прочего, т.е. загрузить железо на полную. А тот уровень, который получился в результате, не слишком отличался от обычных процессоров, в то же время ценник отличался существенно.
Так по идее же ценник VLIW должен быть ниже CISC. Как я понимаю, основное отличие VLIW от CISC в том, что планировщик выкидывается из процессора и переносится в компилятор. Если при этом производительность процессора не меняется — шикарно, компиляция, конечно, дольше, но это не так важно.
Ну вот практика показала, что производительность падает, потому что компиляторы раз за разом оказываются недостаточно хороши, а алгоритмы далеко не все поддаются удобной упаковке для VLIW.
Поток данных не всегда предсказуем априори. Встроенный адаптируется под конкретные сиюминутные данные.
Но это тоже можно закодировать в компиляторе в виде дерева решений. Получится фактически софтовая реализация хардварного планировщика, который будет для каждого конкретного участка кода гораздо проще чем обобщенный хардварный, но зато софтовый предсказатель будет разным в разных местах программы.
Для меня совершенно не очевидно, почему такая реализация принципиально хуже аппаратного планировщика, как пишет Кнут. Под программным планировщиком я понимаю это код, генерируемый компилятором. Что такое есть в аппаратном планировщике, что нельзя эффективно заменить на программную реализацию в компиляторе? Компилятор может решать, когда логика планировки может быть упрощена или вообще не нужна, если код branching-free.
Может, но не всегда. Представьте себе программу управляемую потоком данных с заведомо неизвестным распределением, например интерпретатор, код для которого приходит последовательно из сети например.
Вы не можете предсказать следующую команду и соответственно оптимизировать переходы. Предсказатель переходов работает на актуальной для данного времени статистике, а компилятор ее знать заведомо не может и соответственно не может спланировать бранчи.
В случае сетевого кода и хардварный предсказатель будет бессилен, в этом смысле они окажутся равно неэффективными.
Предсказатель переходов работает на актуальной для данного времени статистике, а компилятор ее знать заведомо не может
Но может рассмотреть варианты точно так же как это делает хардварный предсказатель. Код хардварного предсказателя можно записать программно.
Компилятор принимает пессимистичные решения, т.к. с одной стороны у него нет достаточной инфы по вопросам промахов кэша, ветвлений, некоторых зависимостей по данным (типа алиасов), а с другой при всех его махинациях надо гарантировать корректность состояния программы, в т.ч. корректность состояния на прерываниях. Компилятору «видны» не все возможности паралеллизма, которые «видны» аппаратному. Ну и не стоит забывать, что в обычных процессорах аппаратный планировщик дополняется компилятором, а не исключает его.
Что мешает процессору узнать, есть ли следующая переменная в кэше, а компилятору вставить разные ветки кода на оба случая? То же самое с алиасами. Что есть такого в аппаратном планировщике, что это нельзя полностью перенести в компилятор?
Повторюсь, у компилятора недостаточно тактичекой инфы, этой тактической инфы достаточно у аппаратного планировщика, если бы эта инфа была доступна компилятору, то было бы всё ок. Но т.к. её нет, компилятор вынужден принимать безопасные, пессимистичные прогнозы и генерить менее быстрый код, закладываться на слишком много ситуаций и генерить лишний код для восстановления из них, а в результате и производительность упадёт. В вашем примере про ветки кода компилятор нагенерит лишнего кода, который попадёт в кэш (и сократит его эффективную ёмкость), который приведет к ветвлению (и ударит по конвейеру). Ещё один аргумент — когда «отстреливаются» неудачные предположения — в случае компилятора неправильные предсказания для тех же алиасов придется разруливать полноценными командами, в случае аппаратного планировщика их можно отстрелить на разных стадиях конвейера (когда выполнится только часть команды), установив poison bit или что-то в таком духе, т.е. ущерб от ошибочного предсказания или спекуляции ниже на железном уровне. Наконец, аппаратный планировщик способен передавать результаты (разрешать потенциальные зависимости) в ходе выполнения команд, т.е. 1-я команда готовится записать результат в регистр/память и до этой записи транслирует результат по шинам быстрого доступа командам 5 и 6, которые ждут этого операнда (разрешения зависимости по данным) на разных стадиях конвейера, т.е. разруливать зависимости в ходе выполнения команды не разгружая конвейер (см. алгоритм Томасуло)
у компилятора недостаточно тактичекой инфы
Но компилятор может сгенерировать код планировщика, который будет учитывать тактическую инфу.
компилятор вынужден принимать безопасные, пессимистичные прогнозы и генерить менее быстрый код,
Не обязательно, компилятор может генерировать несколько веток кода, программа может переключаться между ними в зависимости от обстоятельств.
закладываться на слишком много ситуаций
Аппаратный планировщик действует так же.
генерить лишний код для восстановления из них
Который будет лежать в RAM, которой много. Засорения кеша тоже будет, но не факт, что значительное.
компилятор нагенерит лишнего кода, который попадёт в кэш (и сократит его эффективную ёмкость)
Но не сильно. Емкость кеша не так сильно влияет на производительность, что видно по тому, что размеры кэша в современных процессорах с одинаковой IPC могут отличаться в разы, без роста производительности. То есть, что бы засорение кэша повлияло на ситуацию без возможности компенсировать простым увеличением его емкости, неэффективность должна отличаться хотя бы в 3 раза, а то и на порядок.
в случае аппаратного планировщика их можно отстрелить на разных стадиях конвейера (когда выполнится только часть команды),
А толку? Это может немного снизить энергопотребление, но не производительность, ведь спланировать новую полезную нагрузку скорее всего не получится. Да и программируемный планировщик тоже в принципе может такое, теоретически.
Наконец, аппаратный планировщик способен передавать результаты (разрешать потенциальные зависимости) в ходе выполнения команд
это то же самое, что и предыдущий пункт?
Который будет лежать в RAM, которой много. Засорения кеша тоже будет, но не факт, что значительное.
Лишнему коду перед исполнением тоже надо будет попасть в кэш (это требует времени и места), значит часть полезного кода может вытесниться, повысится число промахов.
А толку? Это может немного снизить энергопотребление, но не производительность
Вы серьезно?) С программным планировщиком вам доступна гранулярность уровня команд, не меньше. С аппаратным — гранулярность уровня тактов, а тактов на команду может быть с десяток и на каждом можно отстрелить лишнее, освободить аппаратный ресурс и занять его другой командой (ну, в теории).
это то же самое, что и предыдущий пункт?
Обратите внимание на алгоритм Томасуло. Этот пункт значит, что разрешение зависимостей по данным в случае аппаратного планировщика может происходить гораздо раньше, чем это может позволить программный (потому что гранулярность снова у компилятора не позволяет добраться).
повысится число промахов.
Но на сколько? А если увеличить кэш в два-три раза? Предположим, что число веток, которое рассматривает аппаратный планировщик не превышает 2-3 (поправьте, если не так). Значит и оверхед кода для программного планировщика тоже будет 2-3.
С аппаратным — гранулярность уровня тактов
В случае RISC, а тем более VLIW, команда=такт.
тактов на команду может быть с десяток
Эта проблема (и её героическое преодоление) существует только для CISC. В случае VLIW проблемы нет.
Обратите внимание на алгоритм Томасуло
Обратил, но не понял, какие существенные недостатки это демонстрирует в случае VLIW процессора, где команда=такт. Более того, на сегодняшний день CISC(Intel, AMD) — это RISC+транслятор CISC->RISC.
RISC processors only use simple instructions that can be executed within one clock cycle.
В случае RISC, а тем более VLIW, команда=такт.
Не согласен, у вас там 5-6 тактов на команду, вряд ли меньше, точно не 1, всякие fetch, decode, read operands, execute, write. За один так не уложитесь. В вашей цитате написано про executed, это одна из тех стадий что я привёл, но не единственная.
Но на сколько?
Вот опять же не хочу сильно перескакивать, вы вводите дополнительный код, это приводит к увеличению числа промахов, но не только, это приводит к лишним командам, на которые требуется время выполнения, а также к промахам при предсказании ветвлений. Тут бы весь оверхед посчитать, а не только кэш.
Не согласен, у вас там 5-6 тактов на команду, вряд ли меньше
Откуда Ваши данные? Ребята из Стэнфорда пишут, что RISC -> 1instruction=1cycle:
RISC processors only use simple instructions that can be executed within one clock cycle.

это одна из тех стадий что я привёл, но не единственная.
Так а что в таком случае мешает программному планировщику задействовать алгоритм Томасуло?
То, что гранулярность планировщика == 1 команда, а гранулярность алгоритма Томасуло — 1 стадия. 1 команда требует 5-6 стадий, из этих стадий 1 стадия называется execute.
Что такое стадия и что такое команда? Как они соотносятся с терминами такт, инструкция, cycle, instruction?
В RISC/VLIW 1 instruction=1cycle, соответственно программный планировщик может оперировать на уровне циклов.
Очень люблю, когда о каких-то вещах с жаром спорят люди, которые даже не в курсе базовых понятий темы, на которую они спорят. Но точка зрения имеется, и готовность отстаивать ее с пеной у рта тоже. И то, что если многомиллиардная индустрия в лице Intel не справилась с задачей, то вероятно, задача как минимум очень сложная, никак не смущает.

В RISC/VLIW 1 instruction=1cycle

Повторяю: это не так. Ссылку на википедию про конвейер вам ниже дали.

image
Вот для простоты картинка.
даже не в курсе базовых понятий темы, на которую они спорят
Это Вы про кого ;)?
Повторяю: это не так.
видимо, про себя. Наличие конвеера и многостадийность инструкций — вещи вообще никак не связанные. В RISC, в отличии от CISC инструкции однотактовые, но в обоих случаях есть конвеер. А вот у VLIW, о котором и разговор, как раз отличие от RISC в том, что конвеера нет. Вместе с 1цикловостью инструкций это теоретически даёт возможность делать программный планировщик с гранулярностью в 1 cycle.
если многомиллиардная индустрия в лице Intel не справилась с задачей, то вероятно, задача как минимум очень сложная
Я в курсе, потому и задал изначальный вопрос, ответ на который мне показался очень неочевидным. О тех аргументах, которые Вы приводите я тоже сначала подумал, но вынужден был их отбросить как несостоятельные, так как сам же нашел у них недостатки. Надеялся, что кто-то мне подскажет аргументы получше.
многомиллиардная индустрия в лице Intel не справилась с задачей
С какой задачей? Добиться распространения и коммерческого успеха? Не справилась. Но с технической подзадачей они справились, VLIW процессоры Itanium работали вполне себе неплохо.
В RISC, в отличии от CISC инструкции однотактовые, но в обоих случаях есть конвеер.
Как вы себе представляете конвейер для инструкции в 1 такт? Что там конвейеризируется, в чём смысл? Более того, с железячной точки зрения любопытно как за один такт расшифровать инструкцию, запросить и получить операнды, выполнить инструкцию, записать результаты? С какой частотой такое будет работать в железе?
Вот что я имею ввиду:
While CISC instructions varied in length, RISC instructions are all the same length and can be fetched in a single operation. Ideally, each of the stages in a RISC processor pipeline should take 1 clock cycle so that the processor finishes an instruction each clock cycle and averages one cycle per instruction (CPI).
processor finishes an instruction each clock cycle

Вот на картинке ниже. Каждый такт processor finishes одну инструкцию. При этом выполнение каждой отдельно взятой инструкции занимает четыре такта. Это если без сбросов и простоев, с ними больше.
image

Так понятнее, что такое конвейер и почему «одна иструкция за такт» — это не то, о чем вы думаете?
Это именно то, о чем я думаю, что в среднем выполняется одна инструкция за такт. Я (видимо ошибочно) считал, что у VLIW нет конвейера.
1) У VLIW есть конвейер, и глубокий.
2) Разговор про одну инструкцию за такт начался с вопроса про разную гранулярность аппаратного и программного планировщика.
Аппаратный планировщик имеет гранулярность в один такт, то есть каждый такт смотрит, что происходит со всеми лежащими в конвейере командами, и может принимать решения соответственно.
Для программного планировщика промежуточные результаты выполнения недоступны, поэтому он может сбрасывать конвейер позже, чем смог бы аппаратный, что приводит к значительным потерям в производительности, особенно на сложных ветвящихся алгоритмах, управляемых входными данными.
смотрит, что происходит со всеми лежащими в конвейере командами
В конверее или на подходе? Он может переставлять стадии уже на выполнении? То есть он может проанализировать ситуацию (составить статистику), принять решение (выбрать ветку), и поменять стадии в конвеере за один цикл?
он может сбрасывать конвейер позже, чем смог бы аппаратный
Но гранулярность такая же, для команд, выполнение которых ещё не началось. Переставлять программный планировщик так же может по одной стадии, только задержка между принятием решения и перестановкой операций = длина конвеера. Если процессор суперскалярный, то есть у нас фактически несколько параллельных конвееров, то на одном из конвееров может крутиться планировщик, если надо.
В RISC, в отличии от CISC инструкции однотактовые, но в обоих случаях есть конвеер.

На «CISC» инструкции такие же «однотактовые» ещё с 486.
www.gamedev.net/articles/programming/general-and-gameplay-programming/a-journey-through-the-cpu-pipeline-r3115

А вот у VLIW, о котором и разговор, как раз отличие от RISC в том, что конвеера нет.

Вы вообще не понимаете о чём пишете.
Конвейер нужен для пресловутой «однотактовости» и разумеется у VLIW он точно такой же.
Вы вообще не понимаете о чём пишете.
Развиваете ЧСВ? Я не «пишу», я спрашиваю. Если бы я всё понимал, я бы не спрашивал.
Конвейер нужен для пресловутой «однотактовости» и разумеется у VLIW он точно такой же.
Если так, то понятно.
Плохо они работали, плохо. Как человек занимавшийся оптимизацией под VLIW говорю. Редко какой код мог зайти без ручных оптимизаций. Все надо было пайплайнить, чаще всего руками.
Несчастный компилятор — это всего лишь программа а не провидец. Вершнинг по условиям внутри цикла у него есть, уже одно это приводило к чрезмерному раздутию кода. Плюс, когда у него не было вариантов забить бандл до полного — это затыкивалось нопами. Еще раздутие кода.
Вообщем там был тихий ужас. Что то простое и прямое, с желательно хорошим флопсом — да, работало. Все остальное, особенно бизнес логика, где нет вычислений но куча условий — не-а… И предикаты спасали лишь отчасти.
Гранулярность программного планировщика тоже = 1 стадия. Другое дело, что latency в худшем случае равна длине конвейера, т.к. планировать он может то, что еще не в конвеере. Не знаю, может ли аппаратный планировщик переставлять стадии, которые уже в конвейере.
Гранулярность программного планировщика тоже = 1 стадия.
Не согласен, гранулярность выходит всё равно одна команда. Если считать как вы, получается, что за такт аппаратный планировщик может делать действия с примерно N командами, где N — глубина конвейера. Т.е. его разрешение всё равно выше.
Не знаю, может ли аппаратный планировщик переставлять стадии, которые уже в конвейере.
Частичный ответ на этот вопрос есть всё в том же алгоритме Томасуло, обратите внимание на reservation stations.
Т.е. его разрешение всё равно выше.
Разрешение не выше, одинаково, оно равно одной стадии. Разница только в том, что аппаратный планировщик может переставлять стадии, которые уже в конвеере, а программный — только те, что ещё не попали в него.
аппаратный планировщик может делать действия с примерно N командами, где N — глубина конвейера
Программный и аппаратный планировщики могут выполнять действия с любым числом стадий, если эти стадии ещё не на конвейере. Если на конвейере — только аппаратный, видимо.
Ну нет же, смотрите, аппаратный планировщик может за стадию разрешить несколько конфликтов по данным, передав операнд на 2,3,4 ждущие команды. Программному для достижения этого придется дождаться завершения текущей команды (чтобы записать операнд в регистр), а затем заполнения конвейера командами 2,3,4, которые дождались своего операнда. Т.е. пройдет N+3 тактов до завершения выполнения команды 4 от момента завершения команды 1. Для аппаратного пройдет 4 такта.
затем заполнения конвейера командами 2,3,4, которые дождались своего операнда
Это можно сделать за 1 такт, раз аппаратный может. На самом деле там будет иначе. Компилятор запланирует две похожих последовательности команд по разным адресам, а далее одной командой переключит поток с одной последовательности на другую, в зависимости от результатов предыдущей операции.
Компилятор запланирует две последовательности команд по разным адресам, а далее одной командой переключит поток с одной последовательности на другую, в зависимости от результатов предыдущей операции.
И результат будет получен через N+ тактов, ведь эти последовательности команд (ту последовательность, которую выбрали) надо будет загрузить в конвейер и выполнить, а это как раз N+ тактов.
И результат будет получен через N+ тактов
Согласен, то есть нужно планировать так, что бы смена ветвей была реже чем длина конвейера, что бы конвейеру было чем заняться, пока идёт переключение веток.
надо будет загрузить в конвейер и выполнить
не в конвейер, а в поток исполнения, то есть «перед» конвейером.
Всё же выше я немного обсчитался, но тенденция верна: когда аппаратный планировщик будет находиться на стадии execute команды 1, он уже будет иметь возможность разослать её результат ждущим этого результата в конвейере командам, а затем перейти на стадию write res. На стадии write res он запишет результат в регистр, в этот момент команда 2 уже будет на стадии execute. Программный планировщик (в паре с компилятором) сможет «передать» результат (точнее результат станет доступным в регистре) командам только после завершения стадии write res. Т.е. после этой стадии команда 2 сможет получить свой операнд при условии, что компилятор положил её так что она дошла в этот момент до стадии чтения операндов (пройдя последовательно и fetch, и decode), потом ей надо пройти execute и write res, сравните это со случаем аппаратного планировщика. И нет, по-моему тут второй поток команд будет лишним, одним быстрее, если потока команд будет 2, то конвейер точно придется разгружать.
то есть нужно планировать так, что бы смена ветвей была реже чем длина конвейера
Вот где-то в этот момент компиляторы и начинают проигрывать. Реальные алгоритмы не всегда можно вот так разобрать, особенно для VLIW, который постоянно хочет очень много данных. Такая концепция только для числодробилок хорошо работает.
это приводит к лишним командам, на которые требуется время выполнения
Но они и так выполняются, просто скрытно, на аппаратном планировщике.
Нет, ваш программный планировщик вводит настоящие лишние команды, много, которые надо выполнять. Которые тоже должны будут пройти эти стадии. В аппаратном планировщике эти операции происходят одновременно с какой-либо из стадий, например read operands и т.д., т.е. оверхеда по времени нет.
Эти «лишние» комманды все равно выполняются, просто невидимо для пользователя. Если сделать команды планирования явными (генерируемыми компилятором), то, они будут слабо зависимы от команд основного потока, а значит их можно будет лучше распихивать для более полной загрузки конвейера.
В случае RISC, а тем более VLIW, команда=такт.
Вы в курсе, как конвейер работает?

RISC processors only use simple instructions that can be executed within one clock cycle.
Это просто неправда. Большинство реализаций RISC-процессоров конвейерные, как минимум с тремя, а чаще с пятью-семью ступенями.
Каждый такт заканчивается выполнение новой команды — это обычно так. Но каждая команда выполняется несколько тактов.
Вики устроит? Кстати по вашей ссылке это косвенно тоже можно понять, они там тоже про конвейер упоминают и акцентируются на времени выполнения стадии execute равном в 1 такт.
так там сразу и написано:
Each of these classic scalar RISC designs fetched and tried to execute one instruction per cycle.
В случае VLIW конвеера не предполагается — «non-pipelined scalar architecture», а значит, можно в любой момент выкинуть инструкцию из потока и заменить другой, на уровне каждого цикла.
Each of these classic scalar RISC designs fetched and tried to execute one instruction per cycle.
Еще раз, чтобы вы точно поняли: то, что каждый такт заканчивается выполнение новой инструкции, не означает, что выполнение каждой инструкции длится один такт.

Прочитайте уже, как конвейер устроен, прямо в следующем предложении той статьи в википедии.
Для справки: самый популярный из VLIW, Itanium, имеет девятистадийный конвейер — более глубокий, чем у большинства современных RISC-прроцессоров.
Другой пример. У Вас есть сапоги и ботинки. Вы не можете заранее спланировать что одеть. А в каждый конкретный день, посмотрев в окно одеваете сапоги если дождь идет и ботинки если дождя нет. Погода тут входные данные. Процессор с предсказанием ветвлений одевает ботинки если вчера дождя не было и сапоги если он был и угадывает достаточно часто.
Чем это отличается от программного планировщика, сгенерированного компилятором?
Пример кода:
for (day=0; day<3; ++day) {
  rain=is_raining(day);
  if (rain) wear_waders();
  else wear_sneakers();
}

Допустим is_raining(0)=false, Аппаратный предсказатель будет «опережая» выполнять wear_sneakers(). Что мешает компилятору сделать такой машинный код:
rain0=is_raining(0);
if (rain0) {
  for (day=0; day<3; ++day) { 
    wear_sneakers();
    rain=is_raining(day);
    if (not rain) { unwear_sneakers(); wear_waders();}
}
else {
...
}

Частоту считать можно точно так же.
Сброс неудачной ветки стоит ресурсов, причем иногда очень значительных а компилятор ну никак не может проанализировать данные которых у него просто нет…
сброс ветки стоит ресурсов независимо от ISA, как я понимаю. Компилятор не может проанализировать данные, но он может предусмотреть программный код для анализа этих данных. Я уже привел такой код выше. Программный аналог аппаратного планировщика.
У аппаратного планировщика может быть результат от аппаратного предсказания ветвлений, т.е. он с большей вероятностью угадает переход и сбрасывать не придётся, т.е. для N вызовов этого кода сброс ветки-таки будет стоить меньше в аппаратном случае. Кроме того, эти введенные ветки занимают место в кэше и требуют времени загрузки в кэш, т.е. уже вносят оверхед.
может быть результат от аппаратного предсказания ветвлений, т.е. он с большей вероятностью угадает переход
Почему? Чем программный предсказатель хуже? Всю тактическую информацию он точно так же может учитывать (алиасинг, кэш...).
введенные ветки занимают место в кэше и требуют времени загрузки в кэш, т.е. уже вносят оверхед.
Но много ли? Объем кэша x86 CPU сейчас может различаться в разы без особого роста производительности. То есть при необходимости, в каких-то пределах его можно добавить, если надо.
Почему? Чем программный предсказатель хуже?
Ну хотя бы тем, что аппаратный предсказатель работает с текущей ситуацией (например с историей N последних вызовов данного кода), а программный предсказатель использует инфу на этапе компиляции.
Всю тактическую информацию он точно так же может учитывать (алиасинг, кэш...).
Ценой введения кучи дополнительных инструкций, которые ударят по производительности (ну серьезно, вы предлагаете ввести много лишнего кода, который надо будет исполнить, почему это должно работать быстро?)
программный предсказатель использует инфу на этапе компиляции.
Это не программный предсказатель, это просто компилятор. Программный предсказать выполняется как обычный код, то есть точно так же как и аппаратный предсказатель может динамически считать и учитывать статистику. Отличие в том, что аппаратный предсказатель выполняется на отдельном харде, параллельном ядру и не может менять свою логику, а программный предсказатель выполняется на том же ядре, что и сам полезный код и его логика может меняться на ходу и генерируется компилятором, в соответствии с контекстом, и потому может быть более специальным и эффективным, чем супер-универсальный аппаратный предсказатель.
аппаратный предсказатель выполняется на отдельном харде, параллельном ядру и не может менять свою логику
Почему не может?
Потому что она задается проектировщиками процессора и после изготовления не может быть изменена.
Что значит не может быть изменена? А что мешает сделать логику, управляемую состоянием внутренней памяти планировщика? Или, обоже, нейросеть?
Конечно, планировщик имеет внутреннюю память, при чём тут изменяемость логики его работы? «Код» планировщика не меняется, он может быть сложным, с ветвлениями, но не меняется.
Вы знаете, тут спорить не буду, это больше вопрос определений, то, что назвал я тоже называется программным предсказателем, только наверно мне стоило уточнить, что это статический программный предсказатель (например как здесь).
Ну такой предсказатель и для CISC и для VLIW может работать. Изначальный вопрос был в том, почему невозможно сделать компилятор, который под VLIW будет генерировать код, исполняемый со скоростью CISC.
А почему? В чём оказалась проблема?
VLIW — фирменная «фишка» Эльбрус-2. Далее сплошные «странные» корреляции: лихие девяностые — внезапно появляется IA64, создатель Эльбрусов — лучший почетный работник Intel и т.п.

Поток книг по советскому суперкомпьютеру ( хотя это 1989+ ).

Настал 2008 — «не смогли написать компилятор», эра эвтаназии Itanium.

Совпадения? Не знаю…
Может скажу глупость, но мне кажется есть вариант улучшения «предсказаний» для многопоточных процессоров с использованием машинного обучения. Возможно сегодня это не входит в программу развития, но почему бы не обучить процессор «правильному» угадыванию. Как раньше к процессору пристраивали сопроцессор или графический процессор, так и сегодня можно встроить отдельный блок, который будет мониторить «трафик» инструкций входящих в процессор и обучать нейросеть. Готовые обученные датасеты могут поставляться вместе с процом как некая прошивка с каким-нибудь индексом к модели. Например под Python отдельно, под Javascript отдельно или под какую-то ОС. А потом проц все равно продолжит (или не продолжит) свое самообучение исходя из типичных инструкций которые он получает в реальности и сам может дообучиться и оптимизироваться на лету повышая свою точность в предугадывании инструкций которые следует выполнять или где не стоит тратить на это ресурсы. И через неделю или месяц эксплуатации эффективность угадывания может быть намного выше чем в первый день работы проца.
Я далек от разработки чипов, так что прошу сильно ногами не бить.
Просто глядя на историю развития процессоров за 30-40 лет, и скорость развития нейросетей и машинного обучения в последние годы, мне кажется это вполне возможным направлением для исследования.
А как быстро (за сколько тактов или сколько операций и т.д.) можно получить предсказание от нейронной сети? Сейчас в процессорах вроде механизмы предсказания на основе набора из пары тысяч простейших счетчиков (локальных и глобальных), они при своей простоте быстры и достаточно точны.
так и сегодня можно встроить отдельный блок, который будет мониторить «трафик» инструкций входящих в процессор и обучать нейросеть

Так предсказатели ветвлений на нейросетях уже несколько лет как серийно применяются. Samsung Exynos 8890 тому пример, прямо в вашем мобильнике.
«пропуски кэша»
Это как мясо пропускают через мясорубку?
Перевожу с русского на русский: пропуск кэша -> cache miss -> кэш-промах.

Уважаемый m1rko, пожалуйста, не отказывайте себе в удовольствии погуглить термины, которые вы переводите. Не нужно их выдумывать.
Only those users with full accounts are able to leave comments. Log in, please.