Современные ML-системы опираются на CPU и ускорители — тензорные или графические. Но их производительность часто ограничена пропускной способностью шины между CPU и GPU: данные приходится постоянно перегонять туда-сюда, и выигрыш от ускорителя нередко тает.
Что если есть архитектура, где этого узкого места нет? RISC-V предоставляет гетерогенность принципиально нового уровня, объединяя ключевые компоненты устройства на одном кристалле, что снимает одно из главные ограничений производительности в ML. Но одних процессоров здесь мало — нужна еще экосистема библиотек.
В этой статье Ксения Зайцева, разработчик высокопроизводительных математических библиотек из YADRO, рассказывает, как удалось портировать на RISC-V ключевую для ML-фреймворков библиотеку линейной алгебры Eigen и «малой кровью» оптимизировать ее под векторное расширение RVV, добившись ускорения матричных операций и повышения эффективности обучения моделей.
Чат-бот, который обвалил акции
В конце февраля 2025-го акции крупнейших технологических компаний резко подешевели в общей сложности более чем на один триллион долларов. Сильнее всего досталось NVIDIA: оценка компании снизилась примерно на 600 млрд долларов, что лишило ее статуса самой дорогой в мире. Под давлением оказались и Dell, и Oracle, и Binance.
Ключевые фондовые индексы США тоже просели: Nasdaq на 3%, S&P 500 на 1,5%. Причиной стал китайский чат-бот DeepSeek: разработчики заявили о снижении затрат на его выпуск минимум в 20 раз по сравнению с конкурентами.
Разберем, за счет чего такое удешевление стало возможным и какие технические решения могут дать сопоставимый эффект дальше.

Два пути к оптимизации
Обучение модели — самая ресурсоемкая часть в выпуске таких продуктов, как DeepSeek. Сэкономить здесь можно двумя основными способами.
Разработка новых архитектур нейронных сетей. Этот подход требует глубоких знаний в математике, машинном обучении и серьезных инвестиций в исследования, причем результат не всегда предсказуем.
Использование более мощных и энергоэффективных вычислительных систем. Сейчас для решения ML-задач чаще всего используются гетерогенные системы, когда процессор CPU работает в паре с акселераторами — графическими процессорами или нейроускорителями. Акселераторы выполняют вычисления эффективнее CPU, для этого они и созданы. Однако в такой схеме узким местом становится пропускная способность шины между CPU и акселератором: вычисления на графическом процессоре происходят быстро, но передача данных между CPU и GPU замедляет работу. Поэтому иногда даже полное вычисление на CPU может оказаться быстрее.
Возникает вопрос: можно ли уйти от гетерогенности и обойти это узкое место? Здесь нам могла бы помочь архитектура RISC-V.
Архитектура RISC-V: модульность и гетерогенные чипы
Главная особенность RISC-V — модульность. Она позволяет hardware-разработчикам добавлять только те компоненты, которые нужны под конкретную задачу. В основе архитектуры — базовый набор целочисленных инструкций (32- или 64-битный) для выполнения основных операций. К ним RISC-V позволяет добавлять дополнительные расширения: векторные, матричные, для чисел с плавающей точкой и т. д. Кроме стандартизированных расширений RISC-V дает возможность создавать собственные кастомные расширения, например специально для AI-вычислений.
Фактически процессорное ядро можно собирать как конструктор с нужным функционалом. Это открывает путь к созданию узкоспециализированных ядер на одном и том же базовом наборе инструкций.

На базе RISC-V можно собрать как CPU-ядро общего назначения, так и графический процессор или даже нейроускоритель с кастомными модулями для AI. Большинство типов процессорного ядра может быть построен на базе этой архитектуры.
Такая гибкость открывает путь к созданию гетерогенных чипов — систем на кристалле, где все компоненты объединены в одном устройстве. Они программируются через тот же базовый набор инструкций RISC-V с подключением нужных расширений для каждого из устройств. Главный плюс такого подхода — единая подсистема памяти для CPU и акселераторов, что позволяет обойтись без дополнительных механизмов передачи данных между ними. Уходит основной боттлнек классических связок CPU-GPU — ограниченная пропускная способность шины. За счет этого гетерогенные чипы оказываются намного эффективнее классических гетерогенных вычислительных систем. Кроме того, еще и дешевле.

Таким образом, RISC-V может быть очень актуальна для AI/ML-приложений благодаря трем ключевым особенностям:
Гибкость и расширяемость. Мы можем выбирать только те расширения, которые необходимы для конкретной задачи.
Новая гетерогенность. Архитектура позволяет создавать процессоры, которые объединяют в себе специализированные ядра разных типов. Это реализует принцип гетерогенности на новом уровне — внутри единого процессора, где все блоки могут эффективно взаимодействовать через общую память, устраняя традиционное узкое место таких систем — ограниченную пропускную способность внешней шины.
Open source-архитектура. RISC-V обладает ключевым преимуществом — открытостью. В отличие от проприетарных решений конкурентов, это в первую очередь снимает вопрос лицензионных сборов и устраняет риск потенциального отзыва лицензии. Такая свобода позволяет разработчикам без ограничений кастомизировать архитектуру под конкретные задачи, экспериментировать с новыми идеями и делиться наработками, что в целом ускоряет технологический прогресс.
Однако, как и любая молодая технология, RISC-V сталкивается с вызовами. Главный из них — недостаточно развитая программная экосистема. Те, кто работает с обучением моделей, хорошо знакомы с фреймворками PyTorch и TensorFlow, но не всегда задумываются, что за ними стоят низкоуровневые математические бэкенды, выполняющие основную вычислительную работу. У крупных вендоров такие бэкенды чаще всего проприетарные: например, в экосистеме Intel это oneMKL из состава OpenAI Toolkit, а для GPU свои решения есть у NVIDIA и AMD.

Чтобы ML-приложения на RISC-V были высоко производительными, необходимо оптимизировать под эту архитектуру аналогичные библиотеки с открытым исходным кодом. Один из ключевых кандидатов на оптимизацию — библиотека Eigen.
Eigen — библиотека линейной алгебры
Eigen — это высокопроизводительная библиотека линейной алгебры с открытым исходным кодом. Ее главное преимущество перед другими библиотеками, такими как OpenBLAS, которые предоставляют стандартный C API, заключается в значительно более удобном интерфейсе для разработчиков. Это достигается благодаря тому, что Eigen написана на C++ и активно использует его парадигмы, такие как шаблоны выражений и ленивые вычисления, что позволяет писать лаконичный и интуитивно понятный код, не жертвуя производительностью.
Оптимизация Eigen под RISC-V — это важный шаг для развития экосистемы RISC-V в целом. Но для ML-приложений это важно не меньше, поскольку библиотека используется во многих проектах машинного обучения и компьютерного зрения.

Матричная природа машинного обучения
Линейная алгебра в контексте машинного обучения — это все, что связано с матрицами. Услышав про матрицы в ML, многие сразу думают о полносвязных слоях — и не зря. Но операций линейной алгебры в AI/ML гораздо шире: полносвязные слои, сверточные слои, графовые сети и методы снижения размерности.
Если вы когда-нибудь начнете заниматься профилированием алгоритма машинного обучения, то обнаружите, что как минимум 80% времени работы любого ML-алгоритма приходится именно на умножение матриц. Так что его оптимизация — это как священный грааль для ускорения ML-задач. И поскольку Eigen — один из самых популярных бэкендов для таких вычислений, его оптимизация позволит существенно ускорить AI/ML сценарии в целом.
Оптимизация Eigen под RISC-V
Чтобы эффективно запустить Eigen на архитектуре RISC-V, нужно решить две ключевые задачи:
Базовая интеграция в Eigen оптимизаций под векторное расширение RISC-V Vector Extension (RVV). Спойлер: благодаря архитектуре самой библиотеки это делается быстро.
Адаптация алгоритмов под специфику RISC-V, чтобы максимально использовать ее преимущества.
Как работают векторные расширения
Чтобы процессор выполнил арифметическую операцию, данные нужно сначала загрузить в его регистры — сверхбыструю внутреннюю память.

В классическом варианте для этого используются скалярные регистры: их размер обычно равен машинному слову (например, 64 бита). Они работают по принципу SISD (Single Instruction Single Data): за одну инструкцию обрабатывается один элемент данных — например, складываются два 64-битных числа.
Чтобы ускорить вычисления, разработали подход SIMD (Single Instruction Multiple Data) и векторные регистры, позволяющие выполнять одну операцию сразу над множеством элементов. Например, в архитектуре x86 есть векторное расширение AVX с регистрами шириной 256 бит: за одну инструкцию можно сложить четыре 64-битных числа или восемь 32-битных и так далее. Чем меньше размер элемента, тем больше их помещается в один вектор.
Как работает расширение SIMD
В коде ниже — простая функция сложения двух массивов, написанная с использованием AVX под x86:
void add_arrays(float* a, float* b,
float* result, int size) {
for (int i = 0; i < size; i += 8) {
__m256 vec_a = mm256loadu_ps(&a[i]);
__m256 vec_b = mm256loadu_ps(&b[i]);
__m256 vec_result = mm256add_ps(vec_a, vec_b);
mm256storeu_ps(&result[i], vec_result);
}
}
У AVX векторные регистры шириной 256 бит. Данные — float, значит, в один регистр помещается восемь элементов, поэтому цикл идет с шагом 8. Внутри используются интринсики — специальные функции, дающие доступ к инструкциям процессора. Они разработаны специально для векторных расширений как более удобная альтернатива встроенному ассемблеру.
Логика такая: внутри цикла загрузили два векторных регистра необходимыми данными, выполнили сложение за одну инструкцию, получили третий векторный регистр и сохранили результат в память по указателю.
У этого кода два ограничения. Если длина массива не кратна 8, этот код никак не обработает так называемые «хвосты» данных. Это происходит из-за того, что в традиционных SIMD-расширениях ширина векторного регистра фиксирована, и значит, нужно добавить дополнительную ветку для обработки «хвостов», чаще всего — скалярную. Кроме этого, если мы переходим на архитектуру с другой шириной векторного регистра, необходимо переписать весь код полностью.
SIMD vs RISC-V Vector Extension (RVV)
В RVV подход более гибкий. Ширина векторного регистра не фиксирована и задается параметром VLEN уже во время выполнения программы. Благодаря этому код, написанный и даже скомпилированный под RISC-V, остается переносимым для любой ширины векторного регистра в RVV.
Кроме того, RISC-V снижает накладные расходы при работе с векторными регистрами: количество обрабатываемых элементов в регистре (VL) тоже задается динамически в процессе выполнения. За счет этого «хвосты» данных не требуют отдельной скалярной обработки и дополнительной ветки кода, что упрощает и ускоряет вычисления.

LMUL и работа с регистрами
Отдельно стоит упомянуть еще одну особенность RISC-V — LMUL (Length Multiplier). Этот параметр управляет группировкой физических векторных регистров в логические, что влияет и на скорость работы программы, и на гибкость при использовании векторного расширения.

По умолчанию LMUL = 1 — каждый логический регистр соответствует одному физическому. При LMUL = 2 два соседних физических регистра объединяются в один логический, при LMUL = 4 — четыре и так далее, максимум — LMUL = 8.
По сути, LMUL можно рассматривать как аппаратную поддержку развертки циклов. Развертка сама по себе повышает параллелизм на уровне инструкций, но LMUL дает дополнительный плюс: система воспринимает операцию с объединенным регистром как одну, а не как серию команд для каждого физического регистра, что позволяет сэкономить несколько тактов процессора и уменьшить накладные расходы на вызов.
Как мы внедрили поддержку RVV в Eigen
У тех, кто знаком с низкоуровневыми оптимизациями, может возникнуть вопрос: как вообще оптимизировать целую библиотеку, если код обычно подгоняют под конкретный алгоритм?
Здесь помогает сам C++ и высокий уровень его абстракции. Посмотрим на пример кода, но уже в терминах библиотеки:
void add_arrays(float* a, float* b,
float* result, int size) {
for (int i = 0; i < size; i += traits<Packet>::size) {
Packet vec_a = pload<Packet>(&a[i]);
Packet vec_b = pload<Packet>(&b[i]);
Packet vec_result = padd<Packet>(vec_a, vec_b);
pstore<Packet>(&result[i], vec_result);
}
}
В архитектуре Eigen прямые вызовы интринсиков заменены на шаблонные функции-обертки. Шаблонными параметрами этих функций выступают типы, инкапсулирующие векторные регистры — различные специализации структуры Packet, где каждая специализация соответствует определенному векторному расширению.
Это обобщение не всегда позволяет выжать максимум из конкретного расширения: иногда ради лучшей производительности приходится писать специализации и частично или полностью переписывать отдельные алгоритмы. Зато сама концепция дает главное — возможность быстро добавить поддержку нового векторного расширения без серьезных переделок кода. Именно этим подходом и воспользовались при интеграции RVV в Eigen.
Поскольку в Eigen активно используются шаблоны и ООП, данные типа Packet могут являться полями структур и классов. А значит, их размер должен быть известен на этапе компиляции. Но у RVV ширина векторного регистра (VLEN) — это runtime-значение, которое определяется во время выполнения программы. В результате возникает конфликт парадигм работы с векторами: статической, заложенной в Eigen, и динамической, которую предоставляет RVV. Теоретически можно было бы менять архитектуру самой Eigen, но на это ушло бы много времени. К счастью, существует намного более простое решение.
Как зафиксировать ширину вектора при компиляции под RISC-V
Последние компиляторы под RISC-V поддерживают фиксацию ширины векторного регистра. Для этого нужны дополнительные опции компиляции, например для Clang ++:
-mrvv-vector-bits=<VLEN>
VLEN = {128,256,512,1024, ...}
или для G++:
-mrvv-vector-bits=zvl -march=rv64gcv_zvlVLENb
Конкретный синтаксис зависит от компилятора, суть одна: мы фиксируем VLEN.
В этом случае Eigen не требует серьезных изменений в архитектуре для портирования на RISC-V Vector Extensions. Соответственно, большая часть кода остается неизменной, и это упрощает адаптацию.
Такой код остается переносимым между разной шириной векторного регистра, и, в отличие от классических SIMD-расширений, переписывать реализацию не требуется. Компромисс лишь в том, что под каждое значение VLEN проект все равно придется пересобирать. По сути, мы жертвуем частью гибкости RVV, чтобы портировать библиотеку, изначально заточенную под SIMD.
Дальнейшая работа сводится к переопределению упомянутых оберток. Главная сложность заключается в том, что в RVV отсутствуют некоторые инструкции перепаковки данных внутри вектора (broadcast или shuffle), на которые опирается Eigen. Мы обходим это ограничение, используя существующие инструкции, в частности, сегментные load/store операции, которые позволяют эффективно переупорядочивать данные прямо при загрузке в регистр или сохранении в память. Это решение особенно актуально для работы с комплексными числами, где требуется разделить массив на последовательные вещественные и мнимые компоненты, эффективно заменяя отсутствующую перепаковку.
LMUL и его ограничения в ML
Вернемся к уже упомянутой особенности RISC-V — LMUL.
Как уже говорили, LMUL — это некоторая аппаратная поддержка развертки циклов. Существуют алгоритмы, в которых и развертка, и LMUL дают значительный прирост за счет сокрытия задержек на доступ в память: пока данные загружаются из памяти, процессор выполняет полезные вычисления. Для ряда задач при LMUL = 4 и LMUL = 8 можно добиться ускорения до 25%, что немало.

Но увлечение LMUL — это не всегда хорошо, и злоупотреблять этим не стоит.
Основная проблема в том, что в RVV всего 32 физических регистра. Если объединить их по восемь, остаются лишь 4 логических векторных регистра. При сложных вычислениях процессору может не хватить регистров для промежуточных данных и придется выгружать их в более медленную память, что гарантированно снизит производительность.

На матричном умножении — ключевом хотспоте ML-задач — происходит именно такая ситуация, поэтому LMUL там пользы не дал. Зато помогла другая особенность RVV — инструкция FMA (Fused Multiply-Add).
FMA: умножение с накоплением
FMA — единственная арифметическая инструкция процессора, которая используется в матричном умножении. Она выполняет умножение с последующим сложением за то же время, что занимает простое умножение двух чисел, фактически давая «бесплатное» сложение. Дополнительный плюс — более высокая точность за счет одного этапа округления вместо двух.
Сама идея FMA — это не придумка RISC-V, такие инструкции есть во всех современных процессорах. Нас интересует именно их векторная версия.
Оптимизация умножения матриц
В большинстве SIMD-архитектур все три операнда в векторной инструкции FMA — это векторные регистры. В RISC-V есть более удобная инструкция, позволяющая умножать векторный регистр на скаляр и сразу складывать результат с вектором-аккумулятором.
Чтобы выполнить ту же операцию в традиционных SIMD-архитектурах, сначала приходилось загружать отдельный регистр одним числом и только потом уже использовать стандартный векторный FMA. Это добавляло накладные расходы. По умолчанию Eigen был реализован именно так.

Использовав особенность RISC-V, мы переопределили специализацию матричного умножения для работы с инструкцией vfmadd.vf — умножением вектора на скаляр. Такой подход дал в среднем ≈ 30% прироста производительности на матричном умножении, что напрямую ускорило обучение моделей.
Поддержка float16
Также стоит упомянуть о типе с плавающей точкой половинной точности — float16. Он вдвое меньше стандартного float32 и применяется в задачах, где точность не критична, зато важно время выполнения.
Например, матричное умножение с использованием float16 может быть примерно в три раза быстрее, чем с float32. В ML этот формат становится все популярнее для ускорения обучения моделей, но в основном с помощью GPU. На CPU ситуация другая: там float16 часто эмулируется через float32, что добавляет накладные расходы, особенно в гетерогенных системах.
RISC-V — одна из немногих архитектур, которая предлагает аппаратную поддержку float16 на CPU, открывая новые возможности его применения в ML-задачах. Чтобы использовать это преимущество в Eigen, были определены функции-обертки, как и для других типов данных, а также скалярную арифметику для float16, поскольку по умолчанию Eigen переводил значения в float32 и это негативно сказывалось на производительности.
Вместо заключения
Библиотека Eigen успешно оптимизирована для архитектуры RISC-V (RVV) и поддерживает векторные регистры шириной от 128 до 1024 бит. Конкретный размер регистра должен быть задан пользователем при компиляции.
Поддержка реализована для следующих типов данных:
целочисленные (
int16_t, int32_t, int64_t);с плавающей точкой, включая половинную точность (
_Float16, float, double);комплексные (
complex<float>, complex<double>).
Все изменения оформлены в Merge Request.
Напоследок — о том, что ждет машинное обучение на RISC-V. Помимо уже внедренных векторных расширений, активно разрабатываются стандартные матричные расширения для RISC-V — они должны еще сильнее ускорить ключевые хотспоты ML-задач. Рабочие группы даже удвоили количество встреч с февраля 2025 года, чтобы успеть утвердить новый стандарт к концу года, что говорит о высоких темпах развития и заинтересованности сообщества.
Многие эксперты считают RISC-V одной из самых перспективных архитектур будущего для ML: открытость и гибкость кастомизации делают ее идеальной платформой для разработки специализированных расширений под конкретные задачи. Сегодня RISC-V — важный игрок в сфере машинного обучения, и ее роль в будущем только вырастет.
Скрытый текст
Приходите разбираться в ML на уровне не только фреймворков, но и того, как железо влияет на скорость обучения моделей. В июне 2026-го года состоится профессиональная конференция разработчиков высоконагруженных систем Highload++. Загляните в технический трек — здесь говорят не про хайп, а про то, что реально ускоряет вычисления
Комментарии (2)

zaytsevak Автор
26.11.2025 09:08В железе уже существует ряд кастомных интегрированных (использующих только векторные регистры) матричных расширений. Это, например, SiFive Intelligence Extension или SpacemiT IME (про них немного рассказывалось в этой статье). Еще есть кастомное независимое (использующее именно матричные регистры, независимо от наличия векторных) матричное расширение T-Head RVM. Про него подробно рассказывалось тут.
Стандартные матричные расширения для RISC-V сейчас в разработке. В стандарте планируются как интегрированное, так и независимое матричные расширения. Независимое стандартное матричное расширение должно появиться совсем скоро.
unreal_undead2
А матричные расширения (типа AMX или SME) для RISC V есть/планируются ?