Сможет ли HLS код побить HDL по производительности?
Высокоуровневый синтез (HLS), с его высоким уровнем абстракции и возможностями быстрой валидации, давно продвигается как более эффективный способ реализации проектов для FPGA и ASIC. Тем не менее, по общему убеждению, HLS проигрывает в производительности написанному вручную HDL. С правильным выбором инструментов может оказаться верным обратное утверждение. В этой статье мы задействовали инструменты Silexica SLX для FPGA, чтобы оптимизировать промышленное приложение для обнаружения и отслеживания объектов. Проанализировав приложение на C, SLX помог нам обнаружить критические фрагменты, препятствующие параллелизации и «бутылочные горлышки» при работе с памятью. Используя эти подсказки, мы смогли быстро идентифицировать и перестроить участки кода, сделав его более дружелюбным к используемой платформе. После этого SLX просканировал проект и расставил прагмы HLS для дальнейшей оптимизации приложения. Весь процесс, имплементация, оптимизация и проверка занял у нас одну неделю. Результат был сравнен с версией на написанном вручную HDL, разработка которой заняла два месяца. Окончательный HLS-вариант, оптимизированный SLX, оказался на 64% быстрее HDL-реализации. $CUT$
Введение
Проблемы разработки на FPGA
Емкость FPGA устройств продолжает расти по мере уменьшения геометрии техпроцесса, и возрастающая сложность проектов делает все более трудным использование традиционных маршрутов проектирования, основанных на HDL. Хотя HDL языки и инструменты развиваются, продолжительность циклов разработки оставляет желать лучшего.
Сегодня амбициозные задачи встречаются во всех областях: системы содействия водителю (ADAS), 5G, deep learning, машинное зрение, финтех, авионика и военная техника, etc. Все они подразумевают использование нескольких вычислительных ядер, зачастую вместе со сложными структурами управления. Получающиеся конвейеры обработки данных нелегко реализовать. Изучение архитектурных реализаций с целью нахождения лучшей стратегии с учетом заданных ограничений стоит дорого и занимает много времени.
Более того, давление требований по срокам и стоимости проектирования увеличивают потребность в правильной реализации с первой попытки. Когда узкие места обнаруживаются на поздних этапах разработки, ручной рефакторинг частей проекта делает эти требования практически недостижимыми.
Классический маршрут проектирования для FPGA также страдает при изменении требований к проекту. Действительно, на этапах интеграции и валидации продукта зачастую возникают запоздавшие изменения в спецификации и новые ограничения. Внесение простых добавлений в функционал или небольшое увеличение производительности могут потребовать значительных изменений в проекте. Модификация проекта может потребовать изменение архитектуры, что ведет к переписыванию частей низкоуровневого HDL кода. Само собой, это приводит к длительным циклам повторной верификации и валидации.
Преимущества и проблемы HLS при ускорении проектирования на FPGA
Для решения этих проблем возникли компиляторы высокоуровневого синтеза, позволившие разработчикам переместиться на более высокий уровень абстракции. Они наиболее полезны для сложных компонентов проекта, которые с легкостью могут быть выражены на языках более высокого уровня, таких как C и C++. Высокий уровень абстракции упрощает описание сложных алгоритмов, адаптацию к изменениям функциональности и повторное использование кода.
Помимо ускорения процесса проектирования, главным преимуществом HLS является упрощение верификации. HLS гарантирует, что высокоуровневое описание на C/C++ будет корректно реализовано на уровне RTL, при этом нет необходимости проводить расширенные, отнимающие много времени симуляции на битовом уровне. Более того, поскольку код на С\С++ исполняемый, он может быть использован для проверки приложения на высоком уровне.
Еще одним ключевым преимуществом HLS является возможность быстро исследовать возможности, предоставляемые архитектурой, направляя компилятор с помощью директив, зачастую в форме прагм. Эта возможность дается не просто так: чтобы эффективно направлять компилятор, разработчик должен прежде всего иметь глубокое понимание кода и движения данных по функциям и циклам. Компиляторы HLS - это статические инструменты, которые никак не помогают понять динамические характеристики кода. Более того, поведение компилятора HLS часто трудно предсказать с точки зрения конечной производительности и использования ресурсов. Поэтому разработчики должны вручную исследовать пространство решений, применяя различные прагмы с настройками параметров к соответствующим разделам кода, пока не будут достигнуты цели проекта.
Обзор SLX FPGA
Преимущества SLX FPGA
SLX для FPGA помогает преобразовать ваш C/C++ код в код для FPGA проще, быстрее и с более высокой производительностью. Используя стандартные инструменты HLS от поставщиков FPGA, SLX решает проблемы, связанные с маршрутом проектирования HLS, включая идентификацию несинтезируемого или не подходящего для аппаратной реализации C/C++ кода, обнаружение возможностей для распараллеливания, вставку прагм и оптимальное распределение задач между программной и аппаратной частью.
SLX - это инструмент, который находится над компилятором HLS. Он выполняет динамический анализ исходного кода, чтобы глубже понять его поведение. Инструменты анализа способны рассмотреть код с разных точек зрения, что может быть особенно полезно при попытке понять факторы, ограничивающие производительность. Они анализируют параллелизм в коде, а также потребление памяти и характер доступа к ней. Они могут оценить влияние каждой части программы на общую производительность. Это упрощает разработчикам определение того, нуждается ли код в рефакторинге и где. Затем SLX делает очень важный шаг и использует эту информацию для определения, какие прагмы использовать, где их использовать и с какими параметрами для достижения оптимальной реализации. Более того, SLX может отслеживать потребление ресурсов, исследуя обширное пространство аппаратных возможностей, чтобы сохранить окончательную реализацию в рамках ограничений, заданных разработчиком. Все это происходит автоматически, устраняя необходимость в ручном пошаговом исследовании архитектуры.
Маршрут проектирования SLX для FPGA
На рисунке 1 представлен обзор маршрута для реализации и оптимизации приложения HLS из исходников C\C++.
• Рефакторинг для синтезируемости: SLX выполняет автоматический рефакторинг исходного кода для устранения некоторых часто встречающихся проблем синтезируемости. Для случаев, когда автоматический рефакторинг невозможен, предоставляются подробные подсказки с примерами, которые облегчают переделку кода.
• Обнаружение параллелизма: SLX обнаруживает пригодные для распараллеливания циклы. В настоящее время поддерживаются два шаблона распараллеливания: (1) параллелизм на уровне данных и (2) параллелизм на уровне конвейера. Кроме того, сообщается о блокирующих распараллеливание факторах, например о зависимостях между итерациями одного цикла.
• Анализ приложения: функция анализа SLX призвана помочь пользователям HLS сосредоточиться на наиболее загруженных участках и узких местах системы. Их автоматическое обнаружение осуществляется с точностью до цикла. Количество операций чтения/записи переменных сообщается для каждого адреса.
• Аппаратная оптимизация: SLX выбирает, какая оптимизация подходит для целевой FPGA, например, следует ли раскручивать или конвейеризировать циклы и с каким коэффициентом раскручивания с учетом доступных ресурсов и ограничений интерфейса. Кроме того, на этом этапе принимаются другие важные решения, такие как разбиение массивов, изменение размерности массивов и встраивание функций.
• Вставка прагм HLS: на этом этапе SLX автоматически вставляет прагмы HLS. Разработчики имеют возможность просматривать и редактировать сгенерированные директивы.
Рисунок 1: Обзор маршрута проектирования SLX FPGA
Пример использования: промышленное приложение для отслеживания объектов
Мы разберем пример промышленного приложения обработки изображений, которое разделяет входное изображение на блоки, а затем обнаруживает и отслеживает заданные объекты в каждом блоке. Работа приложения представлена на рисунке 2. Основные этапы работы алгоритма:
• Извлечение блоков из изображений
• Извлечение пикселей из задаваемых областей блоков
• Вычисление суммарных значений для каждого блока
• Сложение линейных параметров
Приложение ориентировано на бюджетные небольшие FPGA, поэтому используемый объем памяти на кристалле должен быть минимизирован. Исходный алгоритм, написанный на C, является хорошим кандидатом для HLS. Исходное изображение HD разделено на отдельные блоки, что делает возможным обширное использование распараллеливания, как обычно и происходит в большинстве алгоритмов обработки изображений. Алгоритм использует целочисленные арифметические операции и арифметику с фиксированной точкой.
Рисунок 2: Обзор приложения
HDL Реализация
Алгоритм был реализован на Verilog с использованием потоковой модели движения данных. Весь алгоритм состоит из нескольких ступеней обработки, связанных FIFO. Каждая ступень обрабатывает входящие пиксели и метаданные перед отправкой на следующий этап. Обработка конвейеризирована; длинные пути комбинаторной логики разбиты на этапы, и для синхронизации между ними используются FIFO. Ядро может обрабатывать пиксели со скоростью 100 мегапикселей в секунду и частотой 100 МГц и поддерживает очень высокую частоту кадров, до 80 кадров в секунду.
Ядро планировалось развернуть на большом количестве устройств, поэтому требовалось тщательное тестирование для сведения риска ошибок к минимуму. Для тестирования и проверки был разработан свой программный пакет. Он выполнял автоматическое сравнение выходных данных эталонной реализации на C с выходными данными Verilog симулятора. Также был разработан набор тестовых сценариев, охватывающих все критические ситуации, с которыми может столкнуться приложение. Разработка HDL заняла почти два месяца до первой проверенной аппаратной реализации. Более того, в ходе работы над проектом спецификации несколько раз менялись. Изменения спецификаций требовали обновлений не только в IP ядре, но и в фреймворке тестирования и проверки.
Рисунок 3: Вид подсказок SLX
Первоначальная реализация HLS
Для оценки SLX для FPGA инженером-специалистом в разработке под FPGA была составлена новая реализация алгоритма обработки изображений на C/C++. Эта реализация обрабатывала данные растрового изображения, как и HDL реализация. Вместо того, чтобы сохранять весь кадр в буфере DDR, пиксели обрабатывались построчно по мере поступления во входной видеопоток. Такой подход позволяет снизить стоимость и избежать потерь энергии, связанных с большой внешней памятью DDR. Имплементация с HLS прошла очень быстро; получение кода для синтеза заняло меньше дня (с помощью некоторых подсказок от SLX). Валидация была выполнена на уровне кода на C и заняла минимальное количество времени. Здесь стоит отметить, что изменения техзадания для исходного алгоритма C, который мы должны были реализовывать и верифицировать на RTL уровне, были бы быстрее реализованы в маршруте проектирования с HLS. Тем не менее, наш первоначальный HLS вариант не удовлетворял нашим целям по быстродействию. Чтобы понять, почему и что мы можем с этим сделать, было важно проанализировать код, и здесь SLX предоставил картину, основанную на его глубоком понимании.
Анализ и оптимизация с помощью SLX
SLX предоставляет множество функций анализа, которые помогают понять перегруженные участки и узкие места приложения. На рисунке 3 показан вид подсказок SLX. Первое, что мы замечаем при автоматическом обнаружении загруженных участков - это цикл на строке 24, занимающий более 96% времени выполнения. Этот цикл распределяет входящие пиксели по блокам. Следующим шагом было выявление узких мест в этом цикле. Для этого мы смотрим вкладку анализа памяти (рисунок 4) и видим, что к переменной bb_lookups обращались 6,2 миллиарда раз; это слишком много для изображения с разрешением HD.
Более того, компилятор HLS по умолчанию выносит эту переменную во внешнюю память (из-за ее размера). Для улучшения ситуации мы изменили код с тем, чтобы проверять только ограниченное подмножество блоков, ближайших к блоку текущего обрабатываемого пикселя. На рисунке 6 показан псевдокод модификации приложения для поиска пикселей в блоках. Количество обращений к bb_lookups снизилось до 170 миллионов (с 6,2 миллиарда), что на 97% меньше, а процент времени выполнения значительно сократился, что показано на рисунке 5 (обратите внимание на снижение процента времени выполнения). Мы быстро проверили, что изменения кода не повлияли на функциональность.
Рисунок 4: Вкладка анализа памяти SLX
На рисунке 4 также показаны другие потенциальные «бутылочные горлышки» при работе приложения с памятью (т.е. переменные большого размера с большим количеством обращений), 'src_image_pixels', 'acc_lpix' и 'acc_ltot'. На рис. 5 показано, что зависимости между разными итерациями одного цикла обнаруживаются по двум из этих переменных. Эти зависимости многое определяют при развертывании или конвейеризации циклов.
Рисунок 5: Вкладка подсказок SLX после первоначального рефакторинга
При синтезе они образуют петлю обратной связи. Если переменная, по которой имеется такая зависимость, хранится во внешней однопортовой памяти, для одной итерации цикла требуется не менее двух циклов, один для чтения переменной из памяти и один для ее обратной записи; следующая итерация не может начаться до завершения этого процесса. Это уменьшает выигрыш от распараллеливания. Однако если переменная хранится в локальном регистре и задержка комбинаторной логики между операциями чтения и записи достаточно невелика, весь процесс может быть завершен в течение одного цикла. Для нашего приложения мы перенесли набор подобных переменных в переменные, относящиеся к локальным блокам исполнения с тем, чтобы они были синтезированы как регистры. Рисунок 6 показывает пример того, как производительность конвейерной реализации цикла улучшается с помощью этой оптимизации путем сбора промежуточных результатов внутреннего цикла в локальных регистрах и их суммирования вне цикла. Это увеличивает пропускную способность конвейера до 1 пикселя за такт. SLX обеспечивает высокоуровневый подход к уточнению самых существенных переменных (с которыми связаны зависимости), в наиболее загруженных циклах приложения.
Рисунок 6: Псевдокод, иллюстрирующий оптимизацию
Установка прагм HLS
Наш последний шаг по оптимизации приложения - дать HLS рекомендации по реализации проекта. В общем случае разработчик должен выяснить, когда и как разбивать и/или изменять размерность массивов, следует ли конвейеризировать или раскручивать каждый цикл (а в случае раскручивания определить правильный коэффициент) и так далее. Алгоритм оптимизации SLX использует внутренние модели вместе с информацией, собранной посредством статического и динамического анализа, для выполнения исчерпывающего исследования пространства возможностей, предоставляемых платформой. Цель состоит в минимизации латентности при соблюдении ограничений на занимаемые ресурсы целевого устройства или других ограничений, заданных пользователем. После нахождения оптимального набора директив SLX показывает рекомендуемые параметры директив для подтверждения разработчика. На рисунке 7 показан мастер преобразования кода SLX, в котором можно выбрать прагмы, которые будут вставлены. В этом примере мы вставили все директивы, автоматически выбранные SLX. В следующем разделе сравниваются результаты различных версий рассматриваемого приложения.
Рисунок 7: Мастер преобразования кода SLX для автоматической вставки прагм
Результаты
На рисунке 8 показана гистограмма латентности различных реализаций на устройстве семейства Xilinx Ultrascale+. Второй столбец на графике - это наша первоначальная HLS реализация; латентность этой версии почти в 90 раз больше, чем у HDL реализации. Третий столбец отображает результат использования SLX для поиска критических узлов и узких мест при работе с памятью и последующего рефакторинга кода. Здесь мы уже видим 20-кратное улучшение по сравнению с первоначальной HLS реализацией; однако это все еще медленнее, чем вариант на HDL. Для четвертой и последней версии мы использовали вставку автоматически оптимизированных SLX директив HLS в отредактированный код. Эта версия даже превосходит HDL реализацию, латентность снизилась на 39%!
Что касается ресурсов, окончательная реализация использует в 2,4 раза больше LUT, почти такое же количество триггеров, на 50% больше BRAM и на 20% больше блоков DSP по сравнению с реализацией на рукописном HDL. Тем не менее, проект по-прежнему помещается в выбранную FPGA, поэтому мы считаем это выгодной ценой за более низкую латентность.
Рисунок 8: Сравнение задержки для различных синтезированных версий
На рисунке 9 показана временная шкала проекта с различными маршрутами проектирования. Традиционный маршрут проектирования на HDL занял почти два месяца, чтобы получить первый проверенный образец; из них четыре недели были потрачены на первоначальное проектирование и кодирование на Verilog, две недели на архитектурные доработки и оптимизацию и еще две недели на тестирование и валидацию. Маршрут с традиционным HLS без SLX занял почти три недели, при этом большая часть времени была потрачена на изучение архитектуры и оптимизацию проекта. Благодаря SLX мы смогли пройти весь цикл менее чем за неделю. Что особенно важно, изменения техзадания могут быть реализованы намного быстрее с маршрутом SLX, чем с HDL или даже традиционным HLS.
Рисунок 9: График проекта, сравнивающий различные потоки разработки
Выводы
Мы использовали SLX для FPGA в качестве инструмента анализа для направления усилий по рефакторингу кода для оптимизации промышленного приложения обработки изображений. Затем мы использовали SLX для автоматического исследования целевой архитектуры и нахождения оптимальных прагм и их параметров для минимизации латентности в рамках ограничений на использование ресурсов. Результаты сравниваются реализацией на написанном вручную HDL. Используя SLX для FPGA и Vivado HLS, мы смогли превзойти производительность HDL реализации на 64%. Реальным преимуществом явилось то, что нам потребовалась всего одна неделя для реализации, оптимизации и проверки исходного приложения на C. По сравнению с HDL этот маршрут проектирования не только давал лучшие результаты за меньшие сроки, но и был гораздо меньше подвержен влиянию изменений в алгоритме, сделанных в последнюю минуту.
Об авторе
Зубайр Вадуд - технический специалист по маркетингу в Silexica GmbH. Он получил докторскую степень по информатике в Левенском университете, Бельгия, в 2014 году; область его интересов включает в себя встроенные системы и высокопроизводительные вычисления. До прихода в Silexica он работал с Mentor Graphics и u-blox.