February 1st, 2007

with Cat The Cat

Совет архитекторам.

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

Это я обнаружил delay instruction в переходах SPARC V8. Ещё и с битом annul, который означает, что не надо выполнять следующую инструкцию, если переход не совершается.

В MIPS такие команды (они называются Branch on ... Likely) стараются запретить и изъять из спецификации. В архитектуре Alpha вообще нет branch delay slot - эта архитектура рассчитывалась на 25 лет использования.

То-то у SPARC всё больше многонитевые, да многоядерные процессоры. Оказывается, всё просто - хрен ты суперскаляр сделаешь. ;)

Ну, тоже хлеб, поскольку двигают индустрию вперёд. ;)
with Cat The Cat

Ещё про микропроцессоры.

Производительности мешают точные исключения.

Например, если у нас есть последовательность a=memory[i];b=memory[j];, то мы не можем выдать вторую команду раньше первой, если есть хоть малейшее подозрение, что первая вызовет исключение (обращение не в ту область памяти).

Точнее, можем, но это бешеные пляски с бубном - например, можно заранее проверить, что memory[i] не выдает исключение (есть для неё область памяти) и взвести специальный бит, который будет за ней тянуться. К тому же нам надо ещё отслеживать, можем ли мы вообще обратиться к memory[i], поскольку значение индекса может быть не готово, не вычислено. Для этого заводится окно предпросмотра и удлиняется такт процессора.

В результате мы не можем выдать зависящие от них инструкции (c=a+10;) и теряем параллелизм на уровне инструкций.

Вот пример на ассемблере:
 # команды, команды, команды.
        add r1,r2,r3    # до этой команды мы не можем даже выдать запрос,
                        # будет ли у нас исключение в нашей загрузке.
        ...
        add r31,r31,r2 # Вот эта...
        sub r30,r31,10 # ...и  вот эта команда блокируют саму загрузку
                       # если мы начнем загрузку раньше, то в r30 может
                       # прийти неправильный результат.
        ...
        ld  r31,0(r1)  # Это наша загрузка.
        ld  r30,4(r1)  # И ещё одна.
Если процессор хочет выдавать опережающие загрузки из памяти или сохранения в память, то ему придётся хорошенько потрудится. Даже для близко расположенных адресов памяти, как в примере выше.

Как сделали в Альфе.

Сдедали очень просто - исключения по обращениям в память создаются, но не выполняются немедленно, а откладываются в буффер. Затем, если встретилась команда "синхронизации исключений," они оттуда выбираются по одному и корректируются. Программным образом, конечно. Если исключений в буфере не было, то эта команда ничего не делает. Если буфер переполняется - из-за агрессивной оптимизации, например, - происходит рестарт процессора. ;)

И под буфер исключений товарищи из DEC не стали заводить дополнительной памяти - отложенные исключения укладываются в кэш инструкций. Данные оттуда можно свободно вытеснить без потери проивзодительности.