Расширения SSE и AVX/AVX2

Последнее обновление: 18.10.2023

SIMD (Single-Instruction, Multiple-Data - "Одна инструкция, много данных") представляет специальный набор инструкций, которые предоставляют параллельную обработку данных. То есть некоторые группы данных могут обрабатываться одновременно, благодаря чему увеличивается производительность и ускоряется выполнение программы. В архитектуре x86-64 инструкции SIMD представлены специальными расширениями SSE/AVX, которые имеют три поколения:

  • Архитектура SSE/SSE2 (Streaming SIMD Extensions), которая предоставляет шестнадцать 128-битных регистров XMM (поддерживают как целочисленные типы данных, так и типы с плавающей точкой)

  • Архитектура AVX/AVX2, которая поддерживает шестнадцать 256-битных регистров YMM (также поддерживают как целочисленные типы данных, так и типы с плавающей точкой)

  • Архитектура AVX-512, которая поддерживает до 32-х 512-битных регистров ZMM.

Архитектуры SSE и AVX поддерживают два основных типа данных: скалярные значения (отдельные числа) и векторы (наборы чисел). Скалярные значения представляют отдельные целые числа или числа с плавающей точкой одинарной или двойной точности. Векторы содержат несколько значений с плавающей точкой или целых чисел (от 2 до 32 значений, в зависимости от типа данных: byte, word, dword, qword, а также регистра и размера памяти) .

Регистры XMM (XMM0-XMM15) могут хранить одно 32-разрядное значение с плавающей точкой (то есть значение типа dword) или четыре значения с плавающей точкой одинарной точности (то есть вектор из 4-х значений). Регистры YMM (YMM0-YMM15) могут хранить 8 чисел с плавающей точкой одинарной точности (32-разрядных):

Хранение чисел с плавающей точкой одинарной точности в SIMD в архитектуре x86-64

Регистры XMM могут также хранить одно число с плавающей точкой двойной точности - .double или вектор из двух значений чисел .double. Регистры YMM могут хранить вектор из четырех чисел с плавающей точкой двойной точности

Хранение чисел с плавающей точкой двойной точности в SIMD в архитектуре x86-64

Аналогичным образом, регистр XMM может хранить вектор с 16 значениями .byte (YMM - вектор с 32 байтами), с 8 значениями word (YMM - вектор с 16 word), 4 числами .long (YMM - вектор с 8 .long) и 2 числами qword (YMM - 4 qword)

Хранение целых чисел в SIMD в архитектуре x86-64

Элементы вектора еще называются дорожками (lane) регистров XMM и YMM. Например, 128-битный регистр XMM может хранить вектор из 16 байтов. Биты с 0 по 7 — это дорожка 0, биты с 8 по 15 — дорожка 1, биты с 16 по 23 — дорожка 2, и так далее. Последние биты со 120 по 127 образуют дорожку 15. 256-битный регистр YMM имеет 32 байтовые дорожки, а 512-битный регистр ZMM имеет 64 байтовые дорожки.

Аналогично 128-битный регистр XMM имеет восемь дорожек размером для типов word (дорожки с 0 по 7). 256-битный регистр YMM имеет шестнадцать дорожек размером в слово (дорожки с 0 по 15). На процессорах с поддержкой AVX-512 регистр ZMM (размер 512 бит) имеет 32 дорожки для типов /word, пронумерованные от 0 до 31. Регистр XMM имеет 4 дорожки размером с .long и dword (дорожки с 0 по 3). Регистр YMM имеет 8 дорожек для .long и dword (дорожки с 0 по 7). Регистр ZMM имеет 16 дорожек размера двойного слова или одинарной точности (номера от 0 до 15).

Проверка доступности расширений для SIMD

Разные процессоры имеют разную поддержку расширений для SIMD. Более новые версии расширений могут быть доступны для более новых версий процессоров. Расширения SSE 4.2 было аннонсировано в 2006, и таким образом, есть очень большая вероятность, что компьютер, на котором будет запускаться программа, поддерживает это расширение (не говооря уже о более ранних версиях SSE). Расширения AVX были предложены в 2008 году, а первый процессор с этими расширениями - Sandy Bridge вышел в 2011 году. Расширения AVX2 вышли в свет с процессором Haswell в 2013 году. Расширения AVX-512, которые добавили поддержку 512-битных регистров, были предложены в 2013 году и были впервые релизованы в процессорах Xeon Phi x200 и Skylake-X в 2015/2016 году.

Чтобы определить, поддерживает ли тот или иной процессор определенные расширения, мы можем применять инструкцию cpuid. Она принимает один параметр, который передавается через регистр EAX. В зависимости от значения, переданного в EAX, эта инструкция возвращает различную информацию о процессоре. Приложение может проверить возвращаемую информацию, чтобы узнать, доступны ли определенные функции процессора.

Например, чтобы узнать поддержку расширений для SIMD, в EAX передается значение 1. В этом случае результат инструкции помещается в регистр ECX. Определенные биты результат могут указывать на поддержку определенных возможностей:

Бит

Описание

0

Доступно SSE3

1

Доступно PCLMULQDQ

9

Доступно SSSE3

19

Доступно SSE4.1

20

Доступно SSE4.2

28

Доступно AVX (Advanced Vector Extensions)

Например, получим эту информацию в программе на Linux:

global _start

section .data
hexstr db "0x123456789ABCDEFG",10,0
len equ $-hexstr    ; размер строки
hexmap db 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 65, 66, 67, 68, 69, 70


section .text
_start:
    mov eax, 1
    cpuid
    mov edi, ecx
    call print_reg

    mov rax, 60
    syscall

; функция печати регистра на консоль
; Параметры: RDI - выводимое значение (64-разрядное число)  
print_reg:
    mov rbx, hexstr     ; начало строки
    mov rsi, hexmap      ; таблица преобразования
    add rbx, 17         ; переходим на первый байт числа
    ; в цикле проходим от RCX = 16 до RCX=1 с приращением -1
    mov rcx, 16  ; 16 символов для печати
forloop: 
    mov rax, rdi
    and rax, 0x0f     ; накладываем маску в AL - получаем первый полубайт
    mov al, byte [rsi + rax] ; получаем символ, который соответствует числу в AL
    mov byte [rbx], al   ; сохраняем один символ ascii
    sub rbx, 1   ; вычитаем из адреса в RBX единицу для перехода к следующему символу
    shr rdi, 4  ; сдвиг влево в RDI для получения следующего байта
    sub rcx, 1     ; уменьшаем счетчик RCX на 1 - для печати следующего символа
    jne forloop           ; переходим к метке forloop, если RCX не равно 1
    ; собственно печать строки с помощью системного вызова write
    mov rax, 1           ; номер системной функци
    mov rdi, 1           ; дескриптор стандартного (консольного) вывода
    mov rsi, hexstr     ; адрес строки
    mov rdx, len         ; размер строки
    syscall                 ; выполняем системный вызов
    ret

Например, на моем компьютере результатом инструкции cpuid является число 0x00000000FEDAB223 или 11111110110110101011001000100011 в бинарной форме. Исходя из установленных битов, можно узнать, какие именно версии расширений поддерживаются.

Дополнительную информацию можно получить, установив значения EAX = 7 и ECX = 0. В этом случае информация с битами будет помещаться в регистр EBX

Бит

Описание

3

Доступно BMI1 (Bit Manipulation Instruction - набор инструкций для манипуляции с битами)

5

Доступно AVX2

8

Доступно BMI2

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

global _start

section .data
hexstr db "0x123456789ABCDEFG",10,0
len equ $-hexstr    ; размер строки
hexmap db 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 65, 66, 67, 68, 69, 70

SSE42Support equ 0x00180000   ; проверяем биты 19 и 20
AVXSupport equ 0x10000000     ; проверяем бит 28
AVX2Support equ 0x20         ; проверяем бит 5

section .text
_start:
    ; Определяем, доступны ли расширения AVX и соответственно регистры YMM
    mov rax, 7
    xor ecx, ecx
    cpuid
    and ebx, AVX2Support     ; проверяем на поддержку AVX2 бит 5
    jnz AVX2         ; если AVX2 доступно, переходим к метке AVX2

    mov rax, 1
    cpuid
    and ecx, AVXSupport    ; проверяем на поддержку AVX бит 28
    jnz AVX         ; если AVX доступно, переходим к метке AVX

    and ecx, SSE42Support     ; проверяем на поддержку SSE42 бит 19 и 20
    jnz SSE42         ; если AVX доступно, переходим к метке SSE42

    mov rdi, 40      ; если ни одно из трех расширений не установлено
    jmp exit
AVX2: 
    mov rdi, 52
    jmp exit
AVX: 
    mov rdi, 50
    jmp exit
SSE42: 
    mov rdi, 42
exit:
    mov rax, 60
    syscall

В данном случае применяем к результату инструкции cpuid одну из масок и по результатам сравнения помещаем в регистр RAX то или иное число.

Полный список соответствия битов в EBX поддержки различных расширений:

  • Бит 00: FSGSBASE (RDFSBASE/RDGSBASE/WRFSBASE/WRGSBASE)

  • Бит 01: IA32_TSC_ADJUST MSR

  • Бит 02: SGX (Software Guard Extensions)

  • Бит 03: BMI1

  • Бит 04: HLE

  • Бит 05: AVX2 (Advanced Vector Extensions 2)

  • Бит 06: FDP_EXCPTN_ONLY

  • Бит 07: SMEP (Supervisor-Mode Execution Prevention)

  • Бит 08: BMI2

  • Бит 09: улучшенные инструкции REP MOVSB/STOSB

  • Бит 10: INVPCID

  • Бит 11: RTM

  • Бит 12: RDT-M (Resource Director Technology Monitoring

  • Бит 13: помечает устаревшими значения FPU CS и FPU DS

  • Бит 14: MPX (Memory Protection Extensions)

  • Бит 15: RDT-A (Resource Director Technology Allocation)

  • Бит 16: AVX512F

  • Бит 17: AVX512DQ

  • Бит 18: RDSEED

  • Бит 19: ADX

  • Бит 20: SMAP (Supervisor-Mode Access Prevention) и инструкции CLAC/STAC

  • Бит 21: AVX512_IFMA

  • Бит 22: Зарезервированно

  • Бит 23: CLFLUSHOPT

  • Бит 24: CLWB

  • Бит 25: Intel Processor Trace

  • Бит 26: AVX512PF (только для Xeon PhiTM)

  • Бит 27: AVX512ER (только для Xeon PhiTM)

  • Бит 28: AVX512CD

  • Бит 29: SHA (Secure Hash Algorithm Extensions)

  • Бит 30: AVX512BW

  • Бит 31: AVX512VL

Регистр управления

Для управления операциями SSE с плавающей точкой в SSE предназначен 32-битный регистр состояния и управления SSE MXCSR. Первые 16 бит имеют определенное значение:

  • 0 (IE): Флаг исключения недопустимой операции. Устанавливается, если была попытка выполнить недопустимую операцию

  • 1 (DE): Флаг исключения денормализации. Устанавливается, если результат операции - денормализованное значение

  • 2 (ZE): Флаг нулевого исключения. Устанавливается, если была предпринята попытка деления на 0.

  • 3 (OE): Флаг переполнения. Устанавливается, если было переполнение.

  • 4 (UE): Флаг потери значимости (underflow). Устанавливается, если была потеря значимости

  • 5 (PE): Флаг потери точности. Устанавливается, если была потеря точности.

  • 6 (DAZ): Денормализованные значения равны 0. Если установлено, денормализованные значения обрабатываются как 0.

  • 7 (IM): Неверная маска операции. Если установлено, исключения недопустимой операции игнорируются

  • 8 (DM): Денормализованная маска. Если установлено, исключения денормализации игнорируются

  • 9 (ZM): Маска деления на ноль. Если установлено, исключения деления на ноль игнорируются

  • 10 (OM): Маска переполнения. Если установлено, исключения переполнения игнорируются

  • 11 (UM): Маска потери значимости. Если установлено, исключения потери значимости игнорируются

  • 12 (PM): Маска точности. Если установлено, исключения точности игнорируются

  • 13-14: Управление округлением. может принимать ряд значений:

    • 00: округление до ближайшего

    • 01: округление до -бесконечности

    • 10: округление до +бесконечности

    • 11: округление до 0 (усечение)

  • 15 (FTZ): Сброс до нуля. Когда установлено, все условия потери значимости устанавливают регистр в 0

Остальне биты 16-32 зарезервированы и в настоящее время не имеют значения.

Доступ к регистру SSE MXCSR осуществляется с помощью следующих двух инструкций:

ldmxcsr mem32
stmxcsr mem32

Инструкция ldmxcsr загружает регистр MXCSR из 32-битной переменной. Инструкция stmxcsr сохраняет текущее содержимое регистра MXCSR в 32-битную переменную. Как правило, данный регистр применяется для установки режима округления.

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850