Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
MProc / M9 / Оптимизация часть 3..doc
Скачиваний:
41
Добавлен:
16.04.2013
Размер:
149 Кб
Скачать

3.3.2.1. Основы зависимости по данным

Следующие основные термины описывают зависимости по данным между инструкциями:

WAW (write after write) – запись после записи. Зависимость между двумя инструкциями, которые записывают один и тот же регистр или место в памяти.

WAR (write after read) – запись после чтения. Зависимость между двумя инструкциями, где инструкция читает регистр или место в памяти, в которое последующая инструкция делает запись.

RAW (read after write) – чтение после записи. Зависимость между двумя инструкциями, где инструкция записывает регистр или место в памяти, которое читается последующей инструкцией.

Неоднозначные зависимости памяти. Зависимости между загрузкой и сохранением, либо между двумя сохранениями, где невозможно определить обращаются ли эти инструкции к перекрывающимся областям. Неоднозначные обращения к памяти включают возможные зависимости WAW, WAR или RAW.

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

3.3.2.2. Зависимость по данным в архитектуре Intel Itanium

Архитектура Itanium требует от программиста включать стопы между регистровыми зависимостями RAW и WAW, чтобы быть уверенном в корректном результатах кода. Например, в следующем коде инструкция add вычисляет значение в r4, которое необходимо для инструкции sub.

add r4=r5,r6 ;; // Группа инструкций 1

sub r7=r4,r9 // Группа инструкций 2

Стоп после инструкции add завершает одну группу инструкций, таким образом, инструкция sub может легально читать r4.

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

mov r16=1

mov r17=2 ;;

st8 [r15]=r16

st8 [r14]=r17;;

Если адрес в r14 равен адресу в r15, то однопроцессорная система гарантирует, что это место памяти будет содержать значение из r17 (2). Следующая зависимость RAW, тоже является легальной внутри одной группы инструкций, даже если программа не способна определить накладываются ли r1 и r2.

st8 [r1]=x

ld4 y=[r2]

3.3.2.3. Планирование инструкций и зависимости по данным

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

add r7=r6,1 // Такт 0

add r13=r25,r27

cmp.eq p1,p2=r12,r23;;

add r11=r13,r29 // Такт 1

ld4 r2=[r3] ;;

sub r4=r2,r11 // Такт 3

Таким образом, задержка загрузки составит два такта, а инструкция sub будет простаивать до третьего такта. Чтобы избежать простоя компилятор может передвинуть загрузку ранее в очереди, так, чтобы машина могла выполнять полезную работу в каждом такте:

ld4 r2=[r3] // Такт 0

add r7=r6,1

add r13=r25,r27

cmp.eq p1,p2=r12,r23;;

add r11=r13,r29;;// Такт 1

sub r4=r2,r11 // Такт 2

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

Теперь предположим, что первоначальная последовательность кода содержала неоднозначную зависимость по памяти между инструкцией сохранения и инструкцией загрузки:

add r7=r6,1 // Такт 0

add r13=r25,r27

cmp.ne p1,p2=r12,r23;;

st4 [r29]=r13 // Такт 1

ld4 r2=[r3] ;;

sub r4=r2,r11 // Такт 3

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

В отсутствие другой архитектурной поддержки, сохранения могут препятствовать перемещениям загрузок и зависимых от них инструкций. Следующие операторы языка C не могут быть переупорядочены, если не известно, что ptr1 и ptr2 указывают на независимые места памяти:

*ptr1 = 6;

x = *ptr2;