Что мы измерили и что нашли

Эта глава — не про Оберон как таковой, а про то, что удаётся узнать, когда система целиком помещается на стол: исходники, компилятор, описание процессора и способ всё это запустить и померить.

Сколько стоит проверка границ массива

Вопрос старый и обычно решается на уровне мнений. Здесь его можно закрыть числом.

Кодогенератор Оберона вставляет перед каждым индексированием по переменному индексу две команды: сравнение и условный переход. В ORG.Mod это переключается одной константой check. Значит можно собрать три компилятора — без проверок, с программными проверками и с аппаратной командой — и прогнать одну и ту же нагрузку.

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

Результат на сборке пяти модулей системы:

конфигурация такты инструкции
без проверок 29 277 745 17 508 073
программные проверки +2.19% +3.33%
аппаратная команда +1.85% +2.78%

Два вывода. Первый: цена проверки границ — около двух процентов, а не десятки, как принято считать. Второй: аппаратная поддержка снимает от этой цены лишь около шестой части (15.5%), потому что сама проверка — это две дешёвые команды, а не работа с памятью.

Та же цифра 2.19% получена независимо, разложением четырёх сборок по схеме «два на два». Два разных способа дали одно число.

Число это — про одну нагрузку. Сколько проверка стоит в вашем цикле, зависит от того, сколько в нём остальной работы: железо экономит ровно одну команду и один такт на индексацию, а доля определяется остальным. В лабораторной №12 вы померите это на своём модуле: соберёте его стоковым компилятором, потом — компилятором, знающим аппаратную команду, и сравните время по Kernel.Time. Таймер машины считает такты (25 000 на миллисекунду), поэтому повтор даёт то же число до миллисекунды.

Аппаратная проверка: куда её впихнуть

Чтобы измерить третью строку таблицы, в процессор пришлось добавить команду. Это само по себе поучительно.

Свободных кодов в RISC5 нет — шестнадцать операций заняты все. Нашлась щель: формат F0, v=1, op=1. Это псевдоним сдвига влево, который компилятор никогда не порождает, потому что для сдвигов всегда ставит v=0. Свободу битов мы не предполагали, а проверили перебором всех значений поля через настоящий декодер.

Предел пришлось разрезать на два куска — по разрядам 27–24 и 15–8, — потому что цельного двенадцатибитного поля не нашлось. Решение приняли по данным: динамический профиль показал, что 68.3% исполнений проверок приходится на массивы длиной 256…1023, которые восемью битами не покрыть. А расхожий ориентир «медиана длины массива — 32» оказался артефактом: это ARRAY 32 OF CHAR, тип имени файла, который встречается в объявлениях часто, а в горячем коде почти не исполняется.

Цена в кремнии: от 46 до 123 квадратных микрон по библиотеке Nangate45, то есть от 58 до 154 вентильных эквивалентов. Нижняя граница лежит внутри шума маршрута синтеза, то есть неотличима от нуля. По частоте знак эффекта определить не удалось честно: на разных маршрутах он получается разного знака.

Три части Project Oberon не согласны между собой

Самая неожиданная находка. Ширина поля смещения в команде перехода:

источник выражение ширина
ORG.Mod, кодогенератор off MOD 1000000H 24 бита
RISC5.v, железо disp = IR[21:0] 22 бита
ORTool.Mod, дизассемблер w MOD 100000H 20 бит

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

А вот таблица условий в дизассемблере ORTool.Mod просто неверна: заполнено одиннадцать индексов из шестнадцати, и два из них противоречат железу — там, где процессор проверяет перенос, дизассемблер печатает «ниже или равно». Проверяется это исполнением: если взять раскладку из ORTool, четыре из ста двенадцати проверок условных переходов на железе падают.

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

Насколько точно предсказуемо время

У RISC5 нет кэша, нет предсказания переходов, нет внеочередного исполнения. Значит время исполнения должно считаться по таблице.

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

Чем всё это проверено

Чтобы числам можно было верить, оснастка проверяется отдельно:

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