- •6. Приложения с плавающей точкой
- •6.1. Краткий обзор
- •6.2. Ограничения эффективности приложений с плавающей точкой.
- •6.2.1. Время ожидания выполнения
- •6.2.2. Пропускная способность выполнения
- •6.2.3. Время задержки при обращении к памяти
- •6.2.4. Пропускная способность памяти
- •6.3. Особенности с плавающей точкой в архитектуре IntelItanium
- •6.3.1. Большой и широкий набор регистров с плавающей точкой
- •6.3.1.1. Примечания относительно точности с плавающей точкой.
- •6.3.2. Инструкции умножения со сложением
- •6.3.3 Программная последовательность деление/вычисление квадратного корня
- •6.3.3.1. Удвоенная точность – деление
- •6.3.3.2. Удвоенная точность – вычисление квадратного корня
- •6.3.4. Вычислительные модели
- •6.3.5. Множество полей состояния.
- •6.3.6. Другие свойства
- •6.3.6.1. Поддержка экранирования операнда.
- •6.3.6.2. Min/Max/Amin/aMax
- •6.3.6.3. Преобразования между целыми и плавающими числами
- •6.3.6.4. Обработка подполей с плавающей точкой
- •6.3.7. Управление доступом к памяти
- •6.3.7.1. Инструкции парной загрузки
- •6.3.7.2. Предвыборка данных
- •6.3.7.3. Управление распределением
- •6.4. Итоги
6.3.6.3. Преобразования между целыми и плавающими числами
Целые числа без знака преобразовываются в свое эквивалентное представление с плавающей точкой путем простого перемещения целого в поле мантиссы регистра с плавающей точкой, используя инструкцию setf.sig. Результирующее значение с плавающей точкой будет в ненормализованном представлении (если только целое число без знака не было больше, чем 263).
Преобразование целого числа со знаком в плавающее и плавающего в целое со знаком, либо без знака, выполняется с помощью инструкций fcvt.xfиfcvt.fx/fcvt.fxu,соответственно. Однако, поскольку целые числа со знаком, преобразуются прямо в их каноническое представление с плавающей точкой, то они не нуждаются в нормализации после преобразования.
6.3.6.4. Обработка подполей с плавающей точкой
Иногда бывает полезно ассемблировать значение с плавающей точкой до его непосредственно составляющих полей. Умножение и деление значений с плавающей точкой на степень двойки, например, могут быть легко выполнены путем соответствующей корректировки порядка. Архитектура Itaniumобеспечивает инструкции, которые позволяют перемещать поля с плавающей точкой между целочисленным и плавающим регистровыми файлами. Деление числа с плавающей точкой на 2.0 выполняется следующим образом:
getf.exp r5 = f5 // Перемещаем S+Exp в int
add r5 = r5, -1 // Вычитаем 1 из Exp
setf.exp f6 = r5 // Перемещаем S+Exp в FP
fmerge.se f5 = f6, f5 // Сливаем S+E w/ с мантиссой
Кроме того, значения с плавающей точкой могут быть созданы из полей различных регистров с плавающей точкой.
6.3.7. Управление доступом к памяти
Признавая тенденцию роста времени ожидания доступа к памяти и затрат на выполнение с высокой пропускной способностью, архитектура Itaniumвключает в себя много архитектурных особенностей для помощи управлением иерархии памяти и увеличением эффективности. Как описано в разделе 6.2, время ожидания памяти и пропускная способность являются существенными ограничителями эффективности приложений с плавающей точкой. Архитектура предлагает особенности для обхода обоих этих ограничений.
Для расширения пропускной способности оперативной памяти в файл регистров с плавающей точкой, архитектура определяет инструкции парной загрузки. Для смягчения времени ожидания памяти, определены инструкции явной и неявной предвыборки данных. Для максимизации использования кэшей, архитектура определяет атрибуты местоположения как часть инструкций доступа к памяти, для помощи в управлении размещением (и удалением) данных в кэшах. Для случаев, где пропускная способность инструкций может стать ограничителем эффективности, архитектура определяет машинные подсказки для уместного запуска предвыборки инструкций.
6.3.7.1. Инструкции парной загрузки
Инструкции парной загрузки с плавающей точкой, позволяют загрузить из памяти два расположенных рядом значения, в два независимых регистра с плавающей точкой. Для того чтобы машина могла использовать только один порт доступа для выполнения модификации регистра, необходимо, чтобы приемные регистры были четным и нечетным физическими регистрами.
Примечание. Ограничение пары четный/нечетный, относится к номерам физических регистров, но не к номерам логических регистров. Программирование с нарушением этого правила вызовет ошибку «Нелегальная операция».
Например, предположим, что машина, которая может выпускать по 2 FPинструкции за один такт, обеспечивает достаточную пропускную способность из кэша второго уровня (L2) для того, чтобы выдержать 2 парные загрузки за один такт. Тогда циклы, которые требуют по 2 элемента данных (по 8 байт каждый) для одной инструкции с плавающей точкой, могут выполняться на пиковых скоростях, когда данные находятся вL2. Обычным примером такого случая является простое точечное произведение с двойной точностью(DDOT – double precision dot product):
DO 1 I = 1, N
1 C = C + A(I) * B(I)
Внутренний цикл содержит две загрузки и умножение-сложение (для накопления произведений С). Цикл ожидал бы выполнения fma, из-за рекуррентного соотношения относительно С. Для устранения рекуррентного соотношения относительно С, цикл обычно разворачивается и используется множество частичных аккумуляторов.
DO 1 I = 1, N, 8
C1 = C1 + A[I] * B[I]
C2 = C2 + A[I+1] * B[I+1]
C3 = C3 + A[I+2] * B[I+2]
C4 = C4 + A[I+3] * B[I+3]
C5 = C5 + A[I+4] * B[I+4]
C6 = C6 + A[I+5] * B[I+5]
C7 = C7 + A[I+6] * B[I+6]
1 C8 = C8 + A[I+7] * B[I+7]
C = C1 + C2 + C3 + C4 + C5 + C6 + C7 + C8
Если используются нормальные (не двойные парные) загрузки, внутренний цикл будет состоять из 16 загрузок и восьми fma. Если мы примем, что машина имеет два порта памяти, то этот цикл будет ограничен доступностью М-слотов и запустится при пиковом соотношении 1 такт на итерацию. Однако, если этот цикл переписать, используя 8 парных загрузок (дляA[I] иA[I+1],B[I] иB[I+1],A[I+2] иA[I+3],B[I+2] иB[I+3] и т.д.) и 8 инструкцийfma, то этот цикл мог бы выполняться при пиковом соотношении 2 итерации за такт (или 0.5 тактов на итерацию) при наличии только двух устройств типа М.