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

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

В этой статье поделюсь нашим опытом запуска нейросетей на DSP процессоре фирмы «Миландр» К1967ВН044, тем более что в новой ревизии появился Ethernet и можно организовать быстрый обмен данными, например, с ПК.



Предпосылки


Большинство читателей, наверняка, усмехнулись и спросили себя: «А почему не...» и дальше произнесли что-нибудь экзотическое, заведомо неподходящее для нейросетевых задач. Но не стоит спешить.
Начнём с простого. Вот давайте посмотрим на каноническую структуру типичной нейросети:



Есть коэффициенты, есть сумматор… А теперь посмотрим на типичную структурную схему цифрового фильтра. Я возьму для примера типовой КИХ-фильтр:



Как в таких случаях принято говорить: «найдите десять различий». Те же умножители, тот же сумматор. Понятно, что структура нейросети приведена уж очень каноническая, реальность сильно отличается. Но уже отсюда видно, что внутренние блоки сигнальных процессоров могут оказаться прекрасными нейроускорителями. Собственно, чтобы подтвердить или опровергнуть эти мысли, и возникла идея попробовать всё сделать на сигнальном процессоре.

А теперь переходим от такого наглядного, но «натянутого» примера к более реальным аргументам.

Важным фактором, по понятным причинам, будет то, что это отечественный продукт, причём его можно купить прямо сейчас. Мы работаем не с программной моделью и не чисто на кончике пера, а с реальным оборудованием.

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

Начнём с тактовой частоты. С одной стороны, по сравнению с лидерами рынка, она выглядит скромно – около 300 МГц. Но это всё-таки вполне на уровне «обычных» микроконтроллеров, а ведь простые нейросети вполне на них работают.

Далее, память – тут уже интереснее. Опять же, кто-то может сказать, что её всего 1.5 МБ. Но ведь это какая память! Благодаря 256-битной шине возможны чтение/запись 32 байт за один такт, что уже гораздо выше возможностей типичного микроконтроллера. При этом имеется и внешняя память, объём которой может составлять десятки или даже сотни мегабайт. И хотя она существенно медленнее внутренней, но находится в том же адресном пространстве, так что при необходимости оба типа памяти можно свободно смешивать при вычислениях.

Теперь – набор инструкций. Очевидно, что для обработки такого скоростного потока данных скалярных инструкций окажется недостаточно – нужны векторные. И они имеются, причём в очень широком ассортименте. Например, процессор умеет за один такт выполнить восемь шестнадцатибитных умножений с получением тридцатидвухбитных результатов. То же самое относится и к другим типичным арифметическим операциям, но не только. Есть и такие, которые обычно отсутствуют, например, векторная упаковка/распаковка чисел разной разрядности и т.п.

Особо стоит отметить, что числа с плавающей точкой – это «родной» формат данных для DSP, так что на них возможно использовать нейросети без квантования.

Ещё одна особенность – способность выполнять до четырёх инструкций за такт. Разумеется, не произвольных, а таких, которые не мешают друг другу. Тем не менее на практике линии из двух-трёх инструкций встречаются достаточно часто.

Фирменная «фишка» DSP процессоров фирмы «Миландр» – это наличие четырёх высокоскоростных LINK-портов. И дело не только в скорости, например, у Ethernet скорость тоже может быть высокой. Но в данном случае речь идёт о передаче не пакетов (с большими накладными расходами на формирование и разбор), а блоков памяти непосредственно между процессорами. То есть можно образовывать матрицы из десятков процессоров, которые будут работать параллельно.

При практической работе, помимо вычислений нам понадобятся интерфейсы для связи с внешним миром. И тут у выбранного DSP тоже всё хорошо: имеется большой набор вариантов, таких как Ethernet, UART, I2C, и т.п. В частности, на штатную плату для разработчика можно подключить экран, камеру и прочую периферию.

Ожидания


Итак, всё вышесказанное позволяет надеяться, что данный процессор позволит получить результаты существенно лучшие, нежели способен обеспечить «обычный» микроконтроллер.

Причём, чтобы было интереснее, мы выбрали в качестве соперника процессор x86 Intel, который хоть и не является специализированным нейроускорителем (соревноваться с ними было бы слишком самонадеянно), но всё-таки обладает сильными чертами.

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

Ведь процессор от Intel имеет многоуровневую систему больших кешей, так что его память, скорее всего, окажется тоже на уровне.

Ну и векторные инструкции в нём имеются, ещё со времён MMX, который появился давным-давно и с тех пор несколько раз развивался.

В общем, мы взяли обычную нейросеть (SsdSlim), которую в дальнейшем будем считать эталоном, адаптировали её код для компилятора MSVS и получили время выполнения 30 мс на процессоре Intel i7 3.4 ГГц.

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

Для работы на нашем процессоре мы использовали среду разработки от Миландра CM-LYNX, которую можно сравнить с MSVC по набору возможностей, то есть графическая оболочка, редактор, компилятор, отладчик и т.п.

Теперь надо повторить запуск на целевом процессоре и сравнить результаты.

Промежуточные результаты


Это оказалось не совсем просто. Казалось бы, мы использовали код, который порождает TVM, то есть портабильный Си. Но возникают проблемы, часть из которых опишу.

Первое затруднение: внутренняя память разбита на 6 банков, между которыми имеются «разрывы». Таким образом, невозможно работать с массивом длиннее 256 КБ. А в коде сети есть тензоры с размерами больше 300 КБ. Если обсчитывать их во внешней памяти, то о производительности можно забыть сразу.

Однако существует простое решение.

Мы придумали объект «блок памяти с дыркой». Он описывается тремя числами: адрес начала, размер первой части, размер промежутка между первой и второй. И выясняется, что адресация в таком блоке чрезвычайно эффективна: достаточно проверить, выходит ли индекс за пределы первой части, и, если да, добавить к нему размер промежутка.
Причём на ассемблере (о котором ещё пойдёт речь) это потребует всего двух инструкций: сравнения и условного сложения, то есть не требуется даже перехода.
И вот, теперь большой тензор располагается в двух банках (даже не обязательно соседних), а адресация выглядит классической, линейной, за счёт класса-обёртки.
Более того, если всё-таки такой тензор не поместился во внутреннюю память целиком, возможно, туда поместится его часть, и мы получим ускорение хотя бы частично.

Кстати, в процессе возникла задача распределять память из нескольких несмежных блоков. У нас есть распределитель, который умеет работать с непрерывным блоком, но его модификация представлялась нежелательной. Возникла идея: при инициализации создать такую структуру данных, чтобы «дырки» с точки зрения распределителя выглядели захваченными блоками. Поскольку на самом деле их никто не захватывал, то их никто никогда и не освободит. Также никто не попробует получить доступ к содержимому. Таким образом, исходный алгоритм полагает, что у него по-прежнему один непрерывный блок памяти, только фрагментированный, и не требует доработки.

Как и следовало ожидать, все данные во внутреннюю память, в итоге, не поместились. Мы были к этому готовы: внутреннюю память следует использовать в качестве кеша. Причем в данном случае алгоритм нейросети очень хорошо для этого подходит: вычисления выполняются по слоям, и каждый слой требует свои данные. Сделаем простую реализацию с выталкиванием во внешнюю память данных, к которым дольше всего не было доступа, и получим время выполнения менее одной секунды.

Теперь давайте посмотрим, чего удалось добиться на текущем этапе.

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



Планы на будущее


Таким образом, отставание на данный момент составляет примерно 25 раз. Вспомним, что у нас тактовая частота более чем в 10 раз ниже, так что разница, обусловленная возможностями процессоров, пока оказывается менее чем в 3 раза в пользу Intel.
Однако его компилятор наверняка «выжимает» из аппаратуры если не максимум возможного, то близко к тому.

А мы ещё толком не начинали.

Дело в том, что если присмотреться к коду после TVM, то мы увидим две особенности.

  1. Код весьма однообразный. Количество самих нейросетевых операций невелико, и обычно они сводятся к вложенным циклам, которые обходят тензор по осям и выполняют некоторое вычисление во внутреннем цикле.
  2. Ассемблерный код, который получается для внутреннего цикла, может быть переписан вручную в виде функции, принимающей на входе конкретные параметры и работающей гораздо быстрее, чем это удается компилятору. Мы реализовали для примера несколько характерных вариантов и получили ускорение около трёх раз.

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

Для этого можно пойти двумя путями.

На первом этапе можно написать скрипт, который преобразует generic код TVM. Дело в том, что исходный код жёстко отформатирован (ведь он является результатом автогенерации), так что не требуется разбор Си-грамматики, достаточно обнаружить и разобрать некий набор шаблонов.

Ну а лучший (но существенно более трудоёмкий) подход – это реализовать в TVM отдельный backend для платформы CM-LYNX, который сам вставит вместо внутренних циклов вызовы ассемблерных функций.

Наконец, есть множество чисто технических моментов для оптимизации, например, организовать загрузку данных из внешней памяти для следующего слоя через DMA, пока считается текущий, организовать обмен данными через Ethernet и т.п.

Выводы


Процессор фирмы «Миландр» ВН044 вполне подходит для эффективного исполнения нейросетей. Конечно, будем реалистами, соревноваться с NVIDIA не получится, но свою нишу занять может. Открытым остается вопрос: какой выигрыш получится от применения многопроцессорной архитектуры? Как покажет себя модуль с четырьмя, восемью, или даже большим количеством процессоров «на борту»?

Комментарии (27)


  1. AndronNSK
    14.05.2024 09:07

    >>если покрыть ассемблерными реализациями хотя бы самые «тяжёлые» операции

    Мрак... Шёл 2024й год...

    Последний на ассемблере для TI С55 я писал в 2010м году. С тех пор у меня и мысли не возникало заниматься таким.


    1. Gryphon88
      14.05.2024 09:07
      +6

      Вот как раз это не беда, особенно когда в качестве цели достаточно экзотичная или новая платформа и компилятор порождает менее производительный код, чем программист. Меня больше смутила цена 1967ВН044 в 30-70 тысяч в зависимости от приемки и исполнения, но я не знаю, сколько стоят аналоги.


      1. EasyLy Автор
        14.05.2024 09:07

        Да, керамика и золото - дорогое удовольствие. К счастью, сейчас уже появился вариант в пластиковом корпусе. Он на порядок дешевле.


        1. Gryphon88
          14.05.2024 09:07
          +1

          Это какая маркировка? Я посмотрел документацию, по ней или в керамике, или бескорпусный.



        1. almaz1c
          14.05.2024 09:07
          +1

          А как обстоят дела с доступностью?

          П.с. увидел сообщение ниже


      1. AndronNSK
        14.05.2024 09:07
        +2

        Если компилятор не порождает эффективный код - это беда. Огромная беда, которая тормозит или делает невозможной разработку конечного продукта вообще.

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


        1. thevlad
          14.05.2024 09:07
          +1

          Компилятор не может порождать эффективный код для SIMD(на x86_64 это SSE/AVX), который выходит за пределы довольно простых паттернов векторизации. Поэтому его пишут на intrinsic которые практически однозначно отображаются в ассемблерные команды (за компилятором остается только распределение регистров) И как-то с "этой бедой" где реально нужна скорость (кодеки/растеризация/криптография/нейросетки на CPU) справляются, а те кто не справляются ищут себе более подходящее занятие.


          1. AndronNSK
            14.05.2024 09:07

            Идите поучите кого-нибудь другого, а не меня. Я компетентен поучить очень многих из здесь присутствующих.


      1. Vladislav_K_Milandr
        14.05.2024 09:07
        +6

        Наверное, это непривычно для российской электроники, но документация размещена на сайте производителя ;) Наименование в пластике: К1967ВН02BG. Розничная цена 5904 руб. с НДС. Доступность образцов в пластике ~ начало июня 2024 г. Доступны образцы и можно уже заказывать вариант в металлокерамическом корпусе К1967ВН044 (Траб. = минус 60 ... + 100 °C).

        https://ic.milandr.ru/products/mikrokontrollery_i_protsessory/protsessory_tsos/k1967vn04bg


        1. Vladislav_K_Milandr
          14.05.2024 09:07

          К1967ВН02BG - образцы (бесплатные) уже доступны.


          1. MechanikArtem
            14.05.2024 09:07

            Бесплатные образцы? Это только для организаций или для для физических лиц тоже есть такая возможность?


            1. NutsUnderline
              14.05.2024 09:07

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


              1. MechanikArtem
                14.05.2024 09:07

                Я посмотрел каталоги на сайте, там вообще много позиций с отметкой new. Для которых тоже можно получить образцы. Например микроконтроллер K1986BE92F1 (аналог stm32f103). Написано, что он аналог K1986BE92QI, который с некоторых пор не выпускается... А мы его использовали в некоторых проектах.


    1. AlexAV1000
      14.05.2024 09:07
      +8

      Если вы не пишете теперь на ассемблере, это не значит, что никто для вас этого уже не сделал. ;-)


      1. AndronNSK
        14.05.2024 09:07
        +3

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

        И второй случай - катастрофа, если мы не говорим о каких сверх массовых устройствах типа STB коробок и видеокодека на асме в ней.


        1. Alexey-N
          14.05.2024 09:07

          Тут наверное правильнее говорить о бизнес модели по продукту. Либо производитель предлагает "вылизанную" библиотеку под организацию нейросетей на своем продукте (вероятно писанную на асме) и таким образом популяризирует свое устройство. Либо за малую толику денежных знаков (вопрос кто платит) находится независимая софтварная компания, которая это делает исходя из своих интересов, и тоже кстати косвенно популяризирует продукт. Либо, как чаще бывает, они объединяют свои усилия.


    1. NutsUnderline
      14.05.2024 09:07
      +5

      лучше писать на ассемблере чем такие коментарии ;) Карму улучшает :) :)


  1. saag
    14.05.2024 09:07

    Intel i7 3.4 ГГц - какого поколения?


    1. EasyLy Автор
      14.05.2024 09:07
      +3

      ОС говорит: Intel(R) Core(TM) i7-3770 CPU @ 3.40GHz


  1. byman
    14.05.2024 09:07
    +5

    Прикольно. Да, с этим компилятором ничего быстрого не получится. Особенно большой проигрыш там , где возможна векторная обработка. Так что да, придется заниматься "тяжелой" атлетикой и применять ассемблер. Правда и с ним не так просто - придется хорошо изучить процессор :)


  1. aabzel
    14.05.2024 09:07
    +2

    Сделайте, пожалуйста, нейросеть, которая сможет по картинке с камеры в Real time отличать гайки от болтов, шайб, гроверов.

    Тогда можно будет сделать прибор для сортировки коробок с метизами.


    1. EasyLy Автор
      14.05.2024 09:07
      +3

      Интересная мысль, спасибо! Занесём в копилку идей, на чём интересном можно отлаживать.

      Правда, именно в копилку. Чтобы сразу начать делать - это нужно готовить датасеты, вести опыты и т.п. Вот так сразу на это время дадут только если проект какой начнётся. Но в целом... Если понадобится новоое что-то делать, то так будет полезней, чем котиков, спасибо.


  1. tminnigaliev
    14.05.2024 09:07
    +4

    Рассказ надо было начинать всё-таки с того, что описываемый вами процессор - это спионеренный Analog Device.

    По описанию похож на ADSP TS201, но у того было 4 линк-порта, так что, возможно, я и ошибаюсь с точным прообразом. Ну просто во-первых, есть такое понятие, как инженерная этика - (если что-то позаимствовано - надо об этом всем честно говорить), а во-вторых, вы же сразу же себе расширите аудиторию, потому что "тигровую акулу" знают многие, а ваш проц - мало кто.

    В целом - спасибо за материал. От работы с TS201 остались очень приятные впечатления. Рад, что всё больше мостиков между DSP и машинным обучением...

    Кстати, роднит их не только похожесть нейрона на КИХ фильтр. Алгоритм адаптации (LMS), придуманный Бернардом Уидроу и его аспирантом Тедом Хоффом - он одинаков и в адаптивных фильтрах и в нейронных сетях, только в нейронках на это накрутили ещё обратное распространение ошибки.

    По поводу бенчмарка против Intel: я в 2011 (примерно) делал на ADSP TS201 один проект. Там у нас была система из 32 таких процессоров, соединённых этими линками. Под мою задачу выделялось 24 процессора. Если их суммарную производительность принять за 100%, то производительность современного на тот момент лэптопного интеловского проца (не помню какого, но со всеми оптимизациями) составляла около 84% Т.е. порядок вашей оценки примерно совпадает с тем, что наблюдал я. Я компилировал и запускал свой проект и на интеле (в первую очередь для отладки кода) и на ADSP ну и замерил перформанс ради интереса.


    1. Vladislav_K_Milandr
      14.05.2024 09:07
      +1

      Прежде всего, большое спасибо за интерес, за отзыв.

      Небольшой комментарий от АО «ПКК Миландр» о появлении в свет DSP серии 1967.

      Спионерить, это скорее взять (купить) топологию 1:1 и сделать вид, что «велосипед» наш)

      В нашем случае процесс выглядит всё же чуть сложнее.

      Топология кристалла, схеомтехника наша, за основу взята только структурная схема, система команд.

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

      В 2015 году в свет вышли К1967ВН044 (близко к ADSP-TS202S. В пластике сейчас это: К1967ВН04BG),  К1967ВН028 (близко к ADSP-TS201S. В пластике сейчас это: К1967ВН02BG), в 2024 году разработали К1967ВН058 (ОКР сдан, к производству ещё не приступили).

      В дополнение к микросхеме нами разработаны отладочные средства, среда отладки CM-LYNX (условный аналог Visual DSP с возможностью конвертер проектов), микросборки из нескольких DSP, радиоаппаратура.

      Согласны с Вами, что родство с TigerSHARC уместно было бы указать. Не учли это, т.к. статью писала сторонняя компания, проводящая эксперимент, с акцентом на реализацию идеи.

      В документации и рекламных материалах прототип всегда стараемся указывать.

      До того на HABR и в профильных журналах выходили статьи, обзоры по DSP серии 1967 и аппаратуре на её основе, более 20 публикаций за 2012 – 2021 гг.

      По части Вашего комментария о техническом содержании статьи возможно сами авторы смогут что-то добавить.

       


      1. Gryphon88
        14.05.2024 09:07

        Можете пояснить порядок именования? Я сначала обрадовался, что К1967ВН044 это "Вэ-Эн 044", а потом растерялся от К1967ВН04BG. Как-то догадываюсь по документации, что BG - в корпусе BGA, но что такое тогда 4?

        У стм32 в разделе даташита ordering information имя мк расшифровывается так:


        1. Sergei2405
          14.05.2024 09:07

          Есть отечественный ГОСТ на обозначение типа корпусов в названии микросхемы. Прошлая редакция кодировала тип корпуса буквой "Я" = BGA, "Ф" = PGA и так далее. В новой редакции вместо букв сейчас используются цифры "5" = QFN, "8"=BGA, "6"=PGA (могу в точных значениях ошибаться, но примерно так). Но к сожалению ГОСТ не предполагает различия по типу материала корпуса (пластиковый и металлокерамический) и если следовать букве ГОСТ, то одна и таже микросхема в пластиковом корпусе и в металлокерамическом будут иметь одно название.

          Так как металлокерамика более строгая - то там по строго ГОСТу. А гражданский пластик решили на свое усмотрение. Примерно так.