- •3. Обращение к памяти
- •3.1. Краткий обзор
- •3.2. Не спекулятивные обращения к памяти
- •3.2.1. Сохранения в память
- •3.2.2. Загрузки из памяти
- •3.2.3. Подсказки предвыборки данных
- •3.3. Зависимости инструкций
- •Раздел 3.4. Описывает определенные в архитектуре Itanium, особенности обращений к памяти, увеличивающие число зависимостей, которые могут быть удалены транслятором.
- •3.3.1. Зависимости по управлению
- •3.3.1.1. Планирование инструкций и зависимости по управлению
- •3.3.2. Зависимости по данным
- •3.3.2.1. Основы зависимости по данным
- •3.3.2.2. Зависимость по данным в архитектуре IntelItanium
- •3.3.2.3. Планирование инструкций и зависимости по данным
- •3.4. Использование спекулятивности в архитектуре IntelItaniumдля преодоления зависимостей
- •3.4.1. Модель спекулятивности в архитектуре IntelItanium
- •3.4.2. Использование спекуляции по данным в архитектуре IntelItanium
- •3.4.2.1. Примеры предварительных загрузок
- •3.4.2.2. Пример кода восстановления
- •3.4.2.3. Краткий обзор терминологии
- •3.4.3. Использование спекуляции по управлению в архитектуре IntelItanium
- •3.4.3.1. Бит NaT
- •3.4.3.2. Пример спекуляции по управлению
- •3.4.3.3. Сливания, заливания и регистр unat
- •3.4.3.4. Краткий обзор терминологии
- •3.4.4. Комбинирование спекуляций по данным и управлению
- •3.5. Оптимизация обращений к памяти
- •3.5.1. Соображения о спекуляции
- •3.5.2. Взаимное влияние данных
- •3.5.3. Оптимизация размера кода
- •3.5.4. Использование постинкрементных загрузок и сохранений.
- •3.5.5. Оптимизация циклов.
- •3.5.6. Минимизация кода проверки
- •3.6. Итоги
3.3.2.2. Зависимость по данным в архитектуре IntelItanium
Архитектура 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;