Блог
Пятнадцать сборок openXC7 с объявленными границами: у малых дизайнов на превращение FASM в битстрим уходит больше времени, чем на трассировку, blinky проиграл умножителю GF, а разброс на побайтово одинаковой работе — 25%.
У всех есть представление о том, сколько занимает открытый тулчейн для Xilinx. Опубликованного числа с приложенным методом я не нашёл — а числа в нашем собственном репозитории оказались оценками, а не измерениями: три разных дизайна с одинаковым временем, по чему и видно, что секундомер никто не включал. Поэтому мы включили.
Границы — это и есть суть, потому что именно на них такие сравнения обычно жульничают.
| Измеряется | Не измеряется |
|---|---|
| синтез yosys | docker pull |
| размещение и трассировка nextpnr-xilinx | git checkout |
| fasm2frames + xc7frames2bit | генерация chipdb |
chipdb собирается один раз отдельной задачей и раздаётся остальным артефактом — он одинаков во всех прогонах и лежит вне окна замера. Это свойство кристалла, а не дизайна, и у Vivado такого шага нет вообще; включать его значило бы соврать в чью-то пользу.
Пять прогонов на дизайн, сид зафиксирован на 1. Машина: общий раннер GitHub ubuntu-latest, 4 ядра. Кристалл: xc7a200tfbg484-2.
| дизайн | медиана | min | max | синтез | P&R | битстрим |
|---|---|---|---|---|---|---|
| gf12_mul | 46.1с | 37.6с | 48.2с | 9.8с | 6.7с | 29.4с |
| blinky | 60.7с | 54.6с | 69.6с | 3.5с | 28.9с | 28.2с |
| gf128_mul | 533.2с | 521.5с | 662.6с | 163.1с | 253.7с | 122.3с |
Мигающий светодиод — 60.7 с. Умножитель GF(2^12) — 46.1 с. Умножитель крупнее по любой мерке и закончил на четверть быстрее.
Причина не в размере. blinky размещается плейсером отжига под ограничение 100 МГц, умножитель — аналитическим плейсером под 5 МГц. Трассировка blinky занимает 28.9 с против 6.7 с у умножителя — вчетверо, и в сторону, противоположную размерам. На этом масштабе время сборки решают плейсер и целевая частота, а нетлист почти не участвует.
Это стоит знать, прежде чем цитировать чей-либо результат на blinky, включая наш.
Генерация битстрима — fasm2frames и следом xc7frames2bit — заняла 28.2 с у blinky и 29.4 с у умножителя. Это 46% и 64% полного времени сборки, и величина почти постоянна: дизайны различаются во всём остальном, а их битстрим-стадия расходится на секунду.
Она ведёт себя как пол. Для чего угодно малого больше времени уходит на превращение FASM во фреймы и фреймов в .bit, чем на размещение и трассировку. Усилия по оптимизации в этом тулчейне идут преимущественно в плейсер и роутер, где и находятся интересные алгоритмы; на малых дизайнах секунды лежат не там.
Пол перестаёт доминировать, когда дизайн достаточно велик. У gf128_mul битстрим — 122.3 с из 533.2 с, то есть 23%, тогда как трассировка занимает 253.7 с, а синтез 163.1 с. Эти две растут вместе с нетлистом, битстрим-стадия — гораздо медленнее.
Каждый прогон одного дизайна выполнял побайтово одинаковую работу: те же исходники, те же ограничения, тот же сид, тот же образ контейнера. Разброс всё равно такой:
| дизайн | min → max | разброс к медиане |
|---|---|---|
| blinky | 54.6с → 69.6с | 25% |
| gf12_mul | 37.6с → 48.2с | 23% |
| gf128_mul | 521.5с → 662.6с | 26% |
Около четверти, устойчиво, на трёх дизайнах с одиннадцатикратной разницей во времени. Это машина — общие раннеры с шумными соседями, — а не тулчейн, потому что тулчейну каждый раз давали один и тот же вход.
Практическое следствие: время из одного прогона может отстоять от медианы на четверть, и внутри этого прогона ничто об этом не скажет. Кто публикует одно число для любого из тулчейнов, публикует выборку из распределения, в которое не заглянул. Включая то число, которое опубликовали бы мы, запустив это один раз.
Первая попытка потеряла все пять задач одного дизайна на синтезе: его обёртка инстанцирует ядро декодера из git-сабмодуля, а задача делала checkout без сабмодулей. Моя ошибка, локальная для одного дизайна — два других свои замеры выдали.
Исправление вышло хуже поломки. Добавленные сабмодули уронили сам шаг checkout, и второй прогон потерял все пятнадцать задач, включая два дизайна, которым сабмодуль был не нужен и которые часом раньше прошли чисто. Этот сабмодуль из CI недоступен вовсе. Декодеры выбыли из набора, их место занял самодостаточный умножитель GF(2^128) — поэтому диапазон размеров в таблице такой широкий.
Третья ошибка до прогона не дошла. В заголовке воркфлоу было написано, что пять повторов нужны из-за чувствительности nextpnr к сиду. Сид зафиксирован, значит повторы измеряют не это, а машину. Останься эта формулировка, пост открывался бы утверждением о плейсере, которого данные не подтверждают.
Колонка Vivado, полученная на тех же трёх дизайнах с теми же границами, на машине, описанной так же точно. Мы её произвести не можем. Дизайны — обычные Verilog и XDC, воркфлоу открыт, так что вторую половину может добавить любой, у кого Vivado есть. А если половины разойдутся так, что ни одна сторона не воспроизведёт другую, — это и будет самым интересным исходом, а не провалом.
Каждая цифра выше измерена, и рядом с ней названы её пределы.