Критика системы команд Эльбрус (продолжение)

И вариант критики команд Эльбрус, используя ассемблерный код мне очень даже понравился. Собственно говоря это более чем логично, критиковать набор команд смотря, на скомпилированные в этот набор команд функции.
Поэтому взял несколько примеров из главы Демонстрация ассемблера от разработчиков Эльбрус.
Ещё нам важен вот этот комментарий (который сделал Armmaster автор статьи):
Нет, там в некоторых ШК стоят операции nop с циферкой, означающей сколько тактов после исполнения данной ШК процессор должен побулькать в ожидании результатов. Если вы все суммируете, то получите 13 тактов
Запомнили - nop выставляет несколько тактов ожидания после команды. Собственно само по себе это лютый треш, который не позволит нормально развивать систему дальше. К примеру - на самых медленных low-power risc-v команда умножения занимает примерно 30 тактов, на более быстрых - один такт. Если бы nop выставлялись ручками, то код не подходил бы для процессоров разной микроархитектуры.
Что-ж, перейдём к первому простому сэмплу. (К сожалению размер картинок будет возможно великоват, но подгонять размер лень.)

код простой, никаких условных переходов, есть несколько независимых операций. Эльбрус должен быть доволен.
Код ассемблера на Эльбрус.

Да это какаято жесть! Почти в каждой команде есть nop и по нескольку тактов. От быстрого процессора мы ожидаем, что он будет выполнять несколько операций за такт. Причем несколько операций должны выполняться КАЖДЫЙ такт. Особенно в таком элементарном коде, без условных переходов.
Для сравнения код ассемблера Risc-V.

Тут знатоки ассемблера Risc-V наверняка заметят, что использовал float вместо double. Но что делать, на моём микроконтроллёрном K210 есть только хардварная поддержка float.
Сравниваем эти два фрагмента кода. Компилятор для Эльбрус сгенерировал 9 широких инструкций. Компилятор для Risc-V скомпилировал 9 инструкций для одного потока. Естественно инструкции Эльбруса занимают в несколько раз больше места, чем инструкции Risc-V. А это нагрузка на подсистему памяти и на кеш процессора. Причем вместе с инструкцией sdivs идёт nop 7, а сразу после неё nop 2. Т.е. суммарно 9 (или 10?) тактов ожидания. Это жесть как она есть, не позволяющая расти архитектуре. А что если завтра захочется выпустить дешевый Эльбрус, у которого будет медленный sdivs? Или разработчики оптимизируют sdivs и она начнёт на такт-другой меньше занимать? Это несовместимость разных поколений будет. Причем замечаем - количество инструкций Risc-V такое-же.
Смотрим следующий сэмпл. Просто перемножение двух чисел. Казалось бы, что проще?

Компилятор Эльбрус выдал нам 3 инструкции, причём одна из них аж с nop 5 (!!!!)

Для сравнения, Risc-V выдаёт ожидаемые две инструкции, причём каждая из них - без обращения в память и гарантированно выполнится за один такт даже на используемом мной микроконтроллёре.

Вот и прикидываем 2 такта на микроконтроллёрном Risc-V или 8 тактов на "суперпроцессорном" Эльбрус. Это дичь! То есть теоретически ядро Kendryte K210 даже на частоте 400 МГц будет при вызове таких функций равняться ядру Эльбрус работающему на частоте 1600 МГц! Это-ж микроконтроллёр, оптимизированный по площади кристалла! Он не оптимизирован на быстродействие. Он просто неспешно выполняет по одной инструкции за такт.
Теперь посмотрим на операцию if

Ассемблер Эльбрус

Ассемблер Risc-V

Тут компилятор от Эльбрус извернулся, и сделал без условных переходов. Это конечно геройство, но по факту чем сложнее код, тем меньше шансов, что это получится. В других архитектурах тоже были такие conditional блоки, но по факту в современное время они отмерли и их заменило спекулятивное выполнение. В данном случае у Эльбрус примерный паритет по IPC с микроконтроллёром.
Ну и напоследок - вызов виртуальной функции.

Ничего жеж сложного не должно быть? Виртуальные функции часто используются.
Смотрим ассемблер Эльбруса.

Внутри 8 инструкций, практически в каждой из которых есть nop. Получается 24 такта на вызов виртуальной функции?????
Вот вызов фиртуальной функции в Risc-V.

Каждая ассемблерная инструкция по делу. Да, тут два чтения из памяти, поэтому функция займёт порядка 6 тактов. Но 6 тактов, это вам не 24 такта. И тут микроконтроллёр уделывает Эльбрус по показателю IPC.
Поэтому лично для меня вывод однозначен - Архитектура Эльбрус крайне неудачна. Почему сравниваю её именно с микроконтроллёром по скорости? Да потому что если сравнивать с чем-то более быстрым, типа современных ARM для телефонов, то там будет ещё x2-x3 разница в скорости. Причем естественно в пользу телефонного ARM. Да, из архитектуры Эльбрус можно что-то выжать, за счёт более широкой шины данных и огромного количества транзисторов используемых в процессоре. Только вот есть мааааааленькая проблемма. В то-же самое количество транзисторов можно напихать в несколько раз больше Risc-V ядер.
PS: Сравнивать гигафлопы на процессорах общего назначения - неблагодарная задача. К примеру у последних процессоров Intel был гиганский скачёк по гигафлопам. Они ввели инструкцию перемножения матрицы на вектор. Вещь логичная, давно напрашивалась. Но есть маааленькая проблемма. Если вынести операцию перемножения матриц в отдельный модуль (называемый нынче модным словом ML acselerator) то можно добиться ещё большего прироста производительности, при меньшем количестве трензисторов задействованных.
|
</> |