Pull to refresh

Comments 47

к 6)

Сигнальный флаг удобно иметь, если вам нужно много считать. В этом случае можно прогнать длиииииннный расчет, а потом проверить установленные флаги караула. ЕМНИП, такой юзкейз предусмотрен для плавучки в Фортране и новом Си.

А разве флаги не сбрасываются после каждой операции?

FPUшные по-моему нет, для этого отдельная команда есть FCLEX/FNCLEX . Ну и к этому самому доступ есть из языка, функция ieee_flags в фортране и концепкция Floating-point environment в C99 и вверх.

6, 7 и 8 — сплошное передёргивание. Перенос почему-то назван «переполнением». Как-то вообще скромно обойдён вопрос длинной арифметики, сдвигов и т.п.
А команды x86 в принципе не допускали потери данных…
Ещё можно было в AVR красиво генерить шим за 4 такта на канал)
ld BL,-Z
cp AL,BL
rol AH
ld BL,-Z
cp AL,BL
rol AH
...
1. Проблема в том, что 99.9% кода сейчас генерируется из языков высокого уровня. А они идут вполне определённым путём, в котором, например, операции типа «сложить два int32_t получив int33_t» не существует. И ARMv8, и RISC-V в этом плане оба ориентируются на ЯВУ, вплоть до того, что RISC-V spec явно говорит «мы максимально близко подтачиваемся под модель памяти C++11».
Чтобы это обойти, надо, например, завести другое средство кроме GCC и LLVM как основное и чтобы на него ориентировались.

2. У них именно переполнение, а не перенос. Перенос важен, да, для, например, слоя mpn в GMP и всех аналогов в других реализациях «длинной арифметики». Там RISC-V будет отставать, бесспорно. Для таких предлагают ввести отдельные команды. Но какая доля таких задач от общего количества? Даже в HPC длинной арифметики сейчас минимум.

> Как-то вообще скромно обойдён вопрос длинной арифметики, сдвигов и т.п.

И со сдвигами точно так же. Какой смысл иметь CF или OF после сдвига, когда он вообще ничего полезного не даёт? (Последний выдвинутый бит для многобитного сдвига — не имеет смысла.)
Для сдвига на несколько бит осмысленным становится признак типа «была ли хоть одна смена знака в процессе сдвига». Но его в x86 нет.
Сдвиг x86 RCL, RCR на >1 бит — туда же никакого смысла.

> Ещё можно было в AVR красиво генерить шим за 4 такта на канал)

Расшифруйте, plz.

Таким образом RISC-V легко превосходит ARM в плотности кода.

Что-то прям вообще какое-то нагибалово беззастенчивое. B арме инструкция одна и размером 32 бита, а сжатые инструкции в RISC-V -- 16 бит каждая, итого суммарно 48 бит (6 байт), да ещё и с ограничениями на доступные регистры. Пожалуй в плотности кода RISC-V точно не превзойти ARM.

Касательно условного исполнения -- мне кажется, всё проще. В рамки парадигмы "отсутствие флагов, трёхадресные команды" условное исполнение никак не вписывается. Так что скорее причина -- именно отсутствие флагов.

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

Сложности с условным исполнением для OoO процессоров конечно есть, и лежат они скорее в той плоскости, что если обычная 3-адресная инструкция зависит по данным только от 2 регистров-источников, то такая же условная начинает зависеть ещё и от регистра флагов и заодно от регистра-приёмника, т.к. по сути она всегда записывает регистр-приёмник, но в зависимости от выполненности условия -- или вычисленным значением или предыдущим неизменным.

Ещё есть вот такие соображения:

  1. Касательно простоты архитектуры RISC-V. Оно конечно так, но только если не смотреть на такие расширения как атомики (куча инструкций типа `AMOADD, AMOOR, AMOXOR` и проч. -- в дополнение к традиционным load-reserved/store-conditional. Зачем???) или то же векторное расширение. Лично я слабо знаком с векторными архитектурами, а чтение описалова этого векторного расширения создаёт стойкое ощущение что без такового знакомства там я мало что пойму. У создалось впечатление, что если базовый набор команд разработала небольшая группа людей, то все эти расширения уже начали разрабатываться 'комитетами' с соответствующими результатами. За примерами аналогичных ситуаций можно далеко не ходить, достаточно вспомнить ситуацию с разными Алголами в прошлом и посмотреть на текущую ситуацию с C++.

  2. Открытость и доступность для реализации архитектуры -- это безусловно хорошо. Но одно из следствий этого -- появления десятков и сотен всеразличных ядер, многие будут со своими собственными расширениями (например кто-то может захотеть приделать собственнные SIMD инструкции вместо монструозных стандартных векторных) и со своими собственными требованиями к компиляторам в части генерации оптимизированного кода. Результатом может стать (или уже становится) невообразимый бардак версий и патчей например того же gcc для каждой реализации архитектуры.

Пожалуй в плотности кода RISC-V точно не превзойти ARM.

Он и не превосходит. Набираете в гугле "risc-v code size vs arm size" и получаете ответ:

Despite the RISC-V RVC extension and compiler optimizations, RISC-V code can be more than 11% larger than the ARM counter- part on embedded-domain benchmarks. :)

У создалось впечатление, что если базовый набор команд разработала небольшая группа людей, то все эти расширения уже начали разрабатываться 'комитетами' с соответствующими результатами.

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

Ну как по мне, базовый RISC-V (типа IMC) вполне себе вкусный проц для какого-нибудь ембедеда. Опять же и роялти не надо платить (если самому запилить ядро или взять опенсорцное) :)

RISC-V -- 16 бит каждая, итого суммарно 48 бит (6 байт)

Что за сумма в 48 бит?

В RISC-V всё инструкции по 32 бита. Даже сжатые 16-битные из С-расширения пакуются попарно в те же 32 бита. Распаковка происходит уже внутри вычислительного ядра.

Экономия за счёт статистически рассчитанного С-расширения как подмножества 32-битного. Отобрали наиболее частые 32-битные инструкции и упаковали покороче. За счёт этого, в среднем, код бинарника в RISC-V становится более плотным.

Выигрыш в плотности кода очень большой по сравнению с х86 и, тем более, разными VLIW. В сравнении с АРМ или MIPS выигрыш тоже есть, но не такой существенный.

Первый же пример в статье -- заменили 1 инструкцию арма на 3 инструкции риск-5. Даже если предположить, что все 3 влезут в 16 бит каждая, суммарно всё равно получается 48 бит (6 байт). А у арма -- 32 бита (4 байта). И тут ещё можно вспомнить, что у arm 32bit ISA тоже есть режим 16- и 32-битных команд (thumb-2).

Что касается x86, то даже беглого взгляда на i386 дизасм (objdump -dS) достаточно, чтобы заметить, что многие инструкции меньше 4 байт (3 или иногда даже 2 байта), а тех, что больше 4 -- очень мало. Конечно, это не относится к amd64, где трындец с префиксами.

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

Выигрыш в плотности у риск5 есть. На этот счёт уже проводились исследования и измерения. По сравнению с х86 код риск5 намного компактнее (как и код АРМ - Эпл давала много таких сравнений). В справнении с АРМ код риск5 тоже чаще всего получается компактнее.

Анализ отдельных команд картинку не даст. Надо смотреть на реальный код и его статистику по бинарникам.

> Что-то прям вообще какое-то нагибалово беззастенчивое.

Не нагибалово, я мерял. RISC-V со сжатыми инструкциями даёт примерно на четверть меньше, чем x86 или aarch64.
(Кастомный сетевой прокси поверх C++.)
Потому что мерять надо на суммарном коде, а не на одной операции.

> Касательно условного исполнения — мне кажется, всё проще. В рамки парадигмы «отсутствие флагов, трёхадресные команды» условное исполнение никак не вписывается. Так что скорее причина — именно отсутствие флагов.

AArch64 отказался от условного исполнения, сохранив флаги.

> (куча инструкций типа `AMOADD, AMOOR, AMOXOR` и проч. — в дополнение к традиционным load-reserved/store-conditional. Зачем???)

Они ж всё объяснили:

>> We provided fetch-and-op style atomic primitives as they scale to highly parallel systems better than LR/SC or CAS. A simple microarchitecture can implement AMOs using the LR/SC primitives, provided the implementation can guarantee the AMO eventually completes. More complex implementations might also implement AMOs at memory controllers

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

> (например кто-то может захотеть приделать собственнные SIMD инструкции вместо монструозных стандартных векторных)

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

Вообще не вижу особого смысла меряться плотностью кода. На 8086 вон можно в два байта (не считая подготовки указателей) скопировать 64 килобайта памяти в другие 64 килобайта памяти — и?.. Объявим x86 наипрогрессивнейшей архитектурой?

смысл в плотности кода есть, чем плотнее код тем эффективнее используется кеш инструкций.

... впору ставить отдельный "контроллер для сжатия инструкций" на этот кеш

Я очень далек от темы, поэтому вопрос от чайника: а насколько это важная проблема для современных CPU у которых и 100Mb кэша может быть?

Важная так как мегабайты кэша только L3. Он сильно медленнее чем L1 и не очень подходит для хранения инструкций.

Крайне важная. размеры кеша инструкций L1 -- порядка 16-32 килобайт, и очень хорошо заметно на unroll'еном цикле, например, когда его размер начинает превышать размер кеша L1 инструкций: сразу падает производительность. Для случая менее плотного кода это случится раньше.

В ARM64 бывает и вот так:


The high-performance cores have an unusually large 192 KB of L1 instruction cache and 128 KB of L1 data cache and share a 12 MB L2 cache.

Apple M1

Чтобы заметить влияние плотности кода нужно чтобы критические циклы не вмещались в кэш первого уровня, т.е. где-то в 32К байта :) Для примера, весь код коремарка меньше 10К. К тому же в процессорах делается все, чтобы подзагрузка кода в кэш 1-го уровня (из кэшей других уровней) была как можно быстрее и желательно заранее. Так что небольшая разница в размере кода это ерунда.

> Чтобы заметить влияние плотности кода нужно чтобы критические циклы не вмещались в кэш первого уровня, т.е. где-то в 32К байта :)

Пользователи kdb+ жалуются на последние версии: движок перестал вмещаться в типовой L1 кэш и от этого сильно замедлился.
Речь идёт о объёмах типа 128KB, 256KB (да, на более мелких процах её не используют в продуктине0.

и пофиг на длительность исполнения самих инструкций....

Туда смотри, сюда не смотри, здесь селедку заворачивали.⁠⁠

Для случая продвинутого OoO процессора, который имеет широкое окно готовых к исполнению инструкций и кучу конвееров для их исполнения длительность исполнения отдельной инструкции уже не играет какую-то особенную роль, тем более при возможном наличии в нём fusing'а инструкций (несколько в одну как в статье описывается) и обратного -- разбивания архитектурных инструкций на несколько внутренних (что издавна делают x86, а также вполне вероятно, что и arm'ы уже тоже).

Вы приводите экстремальный пример. Для определения есть ли смысл, нужно рассматривать не примеры, а среднее влияние.

Очень непоследовательное доказательство. Наша ISA в одном месте не хуже конкурент, ведь есть МОС. А вдругом даже лучше, потому что у конкурента только МОС где у нас специальные команды.

Вы уж определитесь, МОС не хуже чем выделенные команды, или всё же немножечко хуже?

Можно конкретнее? В тексте не совсем так написано. Скорее есть набросы на RISC-М и контр аргументы. Кажется, вы слишком упрощаете.

По второму пункту. Длина команд в RISC-V может достигать до 40 байт. И даже оставлен резерв для более 40 байт длины. Это описано в спецификации. И длина команды по второй версии спецификации определяется в первом 16-разрядном слове. Причём определение длины команды имеет характерные особенности самосинхронизации.

И по последним пунктам. Не стоит путать целочисленный перенос от переполнения... Так как в некоторых задачах перенос вполне нормальная операция. Например, для создания длинной арифметики. В некоторых языках программирования это есть.

По первому и третьему пунктам у меня есть кое-какие свои сомнения преимуществ данных подходов. С одной стороны условное исполнение — это плюс. Но с другой стороны — это минус. Условное исполнение удобно тем, что можно выполнить по флагам и всё такое. А вот для процессора это может быть очень плохой ситуацией. Такие очень плохо поддаются к параллелизации исполнения. Поэтому с этим надо быть осторожным и аккуратным. А слияние в макроинструкции я пока не вижу плюса. Поэтому оставлю этот вопрос открытым. Это надо проверяться временем.

А оставшиеся четвёртые и пятые пункты можно долго рассуждать. Практика может привнести неожиданные сюрпризы. Но в одном я согласен, SIMD у x86/x86-64 — та ещё жестянка.

P.S. Поправил длину байт. Промахнулся с расчётом.

Длина команд в RISC-V может достигать до 40 байт.

А ссылку на спецификацию можете предоставить?
В спецификации расширения «C» я ничего такого не наблюдаю.
riscv.org/wp-content/uploads/2017/05/riscv-spec-v2.2.pdf#page=79

RISC-V standard compressed instruction
set extension, named “C”, which reduces static and dynamic code size by adding short 16-bit
instruction encodings
for common operations.


Не стоит путать целочисленный перенос от переполнения

Последние пункты про переполнение, а не про перенос.
Но в случае беззнаковых чисел это одно и то же.
add t0, t1, t2 # t0 ← t1 + t2
bltu t0, t1, overflow # перейти к overflow, если t0 < t1

Условное исполнение удобно тем, что можно выполнить по флагам и всё такое.

Если очень нужно, используются slt/slti. Ваш флаг будет в регистре и можно выполнять над ним любые операции. В Power есть специальные логические операции для работы с флагами. Такой вот «RISC».

А слияние в макроинструкции я пока не вижу плюса.

А вот разработчики процессоров — видят.
Макроинструкции занимают один слот в ROB, к примеру.

By reducing the number of instructions that must be executed, more work can be done with fewer resources. The idea behind macro-operation fusion is to combine multiple adjacent instructions into a single instruction. A fused instruction typically remains fused throughout its lifetime. Therefore fused instructions can represent more work with fewer bits, free up execution units, tracking information (e.g. in the rename unit), save pipeline bandwidth in all stages from decode to retire, and consequently save power.

en.wikichip.org/wiki/macro-operation_fusion

fortyseven
У вас смысл переведённого искажён. Откуда вы взяли «даже»?
С другой стороны, в лагере ARM отсутствие условных инструкций убивает производительность даже на простых микропроцессорах без OoOE (многие 32-разрядные ARM).

И оригинал. Никаких «даже» тут нет.
The counter-point to this from over at the ARM camp is that by not having conditional instructions you kill performance on simpler In-Order microprocessors (where 32-bit ARM would be used).

Имеется в виду, что ARM лагерь утверждает, что без инструкций условного выполнения на простых in-order процессорах, производительность будет страдать.
(потому как мало транзисторов для реализации эффективного предсказателя).

А ссылку на спецификацию можете предоставить?В спецификации расширения «C» я ничего такого не наблюдаю.riscv.org/wp-content/uploads/2017/05/riscv-spec-v2.2.pdf#page=79

RISC-V standard compressed instructionset extension, named “C”, which reduces static and dynamic code size by adding short 16-bitinstruction encodings for common operations.

Вы по ссылке далеко уехали. Это описывается в начале документации. На 17-18 странице. Далее увидите картинку Figure 1.1: RISC-V instruction length encoding. Это как раз описывает декодирование длины команды. Кстати, при сравнении между спецификациями 2.0 и 2.2 версий длина стала короче в последней. Теперь 192 бита (24 байт) вместо 320 бит (40 байт). Но резерв на большие длины команд остался.

Что за 40 байт? Откуда это тезис?

В спеке риск5 есть глава-заготовка про возможность реализации VLIW, например упаковкой по четыре команды 32х4=128 бит. Но никаких действий и развития в этой части не предполагается. Просто оставлено объяснение, почему такое развитие считается бесперспективным для риск5 сегодня.

Серьёзно? Для чего я выше распинался на 17-18 страницы (это ещё и ссылка на нужную страницу) из спецификации RISC V комментатору beeruser для приведения доказательства своих слов? А также указал различие между старой и новой версии спецификации. Вот в старой версии указывается до 40 байт, а в новой уже — до 24 байт. А возможность использования VLIW в RISC V — это уже отдельный вопрос. Он уже как бы в конце спецификации.

Хотелось бы увидеть примеры инструкции в 40 байт в RISC-V.
Или просто больше 32 бит. Есть такие?

А это уже следующий вопрос. Авторы системы команд RISC V оставили такой задел на будущее, чтобы не постичь такую печальную судьбу x86, переделывая кардинально систему команд. На сегодня пока представлены только на 16 и 32 бита команды. А в будущем, может, появятся и более длинные команды. Но мне кажется, что это будут константные значения или адреса для перехода или получения данных. А если вспомнить, что RISC V в будущем легко может стать 128-битным процессором. Ибо тоже есть задел под такое.

Это не следующий, а основной вопрос))

Зацепы на развитие архитектуры риск5 в спеке конечно есть. И основания есть и прописаны хорошо. А реализации нет и не предвидится. Чему тоже есть обоснование.

Собственно ставка в архитектуре риск5 на расширяемость вполне в балансе с прагматичностью подхода к реализации расширений.

Поэтому сейчас говорить о какой-либо широкой команде длинной в зарезервированные, но мифические 40 байт просто не приходится. Гораздо более вероятно, что существующего 32-битного кодирования хватит очень-очень надолго.

Гораздо более вероятно, что существующего 32-битного кодирования хватит очень-очень надолго.

Невольно вспоминаются полумифические "640 килобайт хватит на всё" и более актуальное, но не факт, что существовавшее в реальности "4 байта на адрес хватит для всех компьютеров в Мире"...

Как раз на этот счёт оговорка про надолго :)

Вся история внутри риск5 про возможность расширения "вширь и вглубь". Но только при просчитанной полезности/целесообразности.

И поэтому, по прошествии 10 лет, 32 бит всё ещё достаточно для кодирования команд, включая работу с 64-битными операндами. Может и парадоксальным кажется, но факт!

Но мне кажется, что это будут константные значения

Такие вещи нужно было изначально делать. Потому как загрузка констант это базовая возможность ISA. Следствием их введения будет потеря совместимости с базовым набором, что аналогично полной смене ISA.
Да и как показывает практика, длинные константы редко когда нужны.

А если вспомнить, что RISC V в будущем легко может стать 128-битным процессором. Ибо тоже есть задел под такое

Для этого не нужно увеличивать длину команды.
five-embeddev.com/riscv-isa-manual/latest/rv128.html

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

А как же засунуть 128-битную константу в команду (то есть загрузка 128-битной константы в регистр)? Конечно, можно использовать косвенную адресацию для загрузки данных или пачками команд мелких констант со сдвигом. Но всё же.

> А как же засунуть 128-битную константу в команду (то есть загрузка 128-битной константы в регистр)?

Они и на 64 не дают метода. Только на 32. Решили, что не нужно.

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

Именно. Считается, что программа длиннее 2GB не бывает.

Так система команд ещё развивается. Рановато об этом говорить. Им для начала добить актуальные задачи.

Косвенная адресация не только для прыжков по всей программе, но для работы с данными. Особенно массивы. А они как раз могут быть очень большими.

Я имел в виду следующее. Загрузить 32-битную константу можно в 2 команды: lui + addi.
Для 64-битной, если бы потребовалось, можно было бы в стиле ARM — 4 команды (movz + 3 штуки movk). Но есть альтернатива: если до хранилища константы можно добраться по смещению ±2GB, то работает вариант:
auipc rt, старшая часть смещения
ld rd, младшая часть смещения (rt)
Итого 2 команды. (Для ARM тоже могут так делать, с поправкой на auipc -> adrp и её шизу с урезанием младших бит.)
Теперь для 128: или набираем уже 8 штук (movz + 7*movk), или те же auipc + ld. А константа где-то рядом.
Спрашивается, «зачем платить больше», если можно 2 команды в любом случае?

Ну да, можно было бы подумать сделать тут, например, 10-байтную команду с 8-байтной константой. Но это на сейчас будет единственная 10-байтная команда. Ради неё наворачивать отдельный блок в декодере?

Ну да, цена — промежуточный регистр. Но тут их 31, подобрать всегда можно.

Я уверен, что авторы RISC-V руководствовались этой логикой 1:1. Даже вроде что-то подобное читал.

> Особенно массивы. А они как раз могут быть очень большими.

А как это мешает? Всё равно смещение в самой команде это -2048..+2047, остальное надо получать сложив базу и индекс в промежуточный регистр.
Вот если бы сделали адресацию по двум регистрам и смещению, как в SystemZ или x86 — да, стало бы проще. Но сомневаюсь, что это скоро будет.

То есть получается достаточно длинная дискуссия. Или одна команда или несколько. Для длинной константы. Вопрос в том, как повлияет на картину производительности.

Но, в целом, получается дополнительная операция и/или оптимизация, чтобы держать базовые значения в поблизости.

Перевод instruction как "инструкция" прямо глаза режет. Вроде ж начали с "команд", зачем чередовать?

По поводу кода в п.7 - там все просто: если вы к одному положительному числу прибавляете другое положительное и результат оказывается меньше одного из чисел (по сути любого), то тут что-то не так... т.е. произошло переполнение. (первый пример)

Остальное - по той-же логике.

Sign up to leave a comment.