Streaming SIMD Extensions
Definicja intuicyjna |
SSE to dodatkowe rozkazy rozpoznawane przez mikroprocesory firmy Intel oraz kompatybilnych, które pozwalają znacznie szybciej wykonywać obliczenia matematyczne, szczególnie te wykorzystywane w dziedzinie multimediów, co przekłada się na zwiększenie efektywności działania m.in. gier komputerowych, programów graficznych, muzycznych, kodowania filmów i muzyki. |
![](http://upload.wikimedia.org/wikipedia/commons/thumb/7/7e/SSE_registers.svg/150px-SSE_registers.svg.png)
SSE (ang. Streaming SIMD Extensions) jest nazwą zestawu instrukcji wprowadzonego w 1999 roku po raz pierwszy w procesorach Pentium III firmy Intel. SSE daje przede wszystkim możliwość wykonywania działań zmiennoprzecinkowych na 4-elementowych wektorach liczb pojedynczej precyzji (48 rozkazów). Ponadto wprowadzono jedenaście nowych rozkazów stałoprzecinkowych w zestawie MMX, a także dano możliwość wskazywania, które dane powinny znaleźć się w pamięci podręcznej.
SSE to również zmiany w architekturze procesora: dodano 8 rejestrów XMM o rozmiarze 128 bitów oraz 32-bitowy rejestr kontrolny MXCSR; w 64-bitowych wersjach procesorów (AMD64, EM64T) dostępne jest jeszcze 8 dodatkowych rejestrów XMM. Rejestry 128-bitowe, na zawartości których wykonywana jest większość rozkazów SSE (nazywane w asemblerze xmm0, xmm1, ..., xmm15), stanowią zupełnie odrębne komórki pamięci – w odróżnieniu od rejestrów MMX nie zostały zamapowane na inne rejestry.
Typy danych
SSE wprowadza nowy typ danych: 4-elementowy wektor liczb zmiennoprzecinkowych pojedynczej precyzji (ang. 128-bit packed single-precision floating-point); liczba zmiennoprzecinkowa ma rozmiar 32 bitów. Poza tym wykorzystuje typy wektorowe zdefiniowane w MMX.
Rozkazy SSE mogą wykonywać działania arytmetyczne na wektorach liczb zmiennoprzecinkowych na dwa sposoby:
- packed (równoległe) – wykonując równocześnie 4 niezależne działania zmiennoprzecinkowe na odpowiadających sobie elementach wektorów;
- scalar (skalarne) – wykonując działanie tylko na pierwszych elementach wektorów.
Przykład – mnożenie dwóch wektorów (rozkazem MULPS xmm0, xmm1
):
+-------+-------+-------+-------+ | x3 | x2 | x1 | x0 | xmm0 +-------+-------+-------+-------+ * * * * +-------+-------+-------+-------+ | y3 | y2 | y1 | y0 | xmm1 +-------+-------+-------+-------+ = = = = +-------+-------+-------+-------+ | x3*y3 | x2*y2 | x1*y1 | x0*y0 | xmm0 +-------+-------+-------+-------+
Przykład – mnożenie pierwszych elementów wektorów (rozkazem MULSS xmm0, xmm1
):
+-------+-------+-------+-------+ | x3 | x2 | x1 | x0 | xmm0 +-------+-------+-------+-------+ * +-------+-------+-------+-------+ | y3 | y2 | y1 | y0 | xmm1 +-------+-------+-------+-------+ = = = = +-------+-------+-------+-------+ | x3 | x2 | x1 | x0*y0 | xmm0 +-------+-------+-------+-------+
Mnemoniki rozkazów
Mnemoniki instrukcji SSE działających na wektorach liczb całkowitych zostały wybrane zgodnie z konwencję wprowadzoną w MMX – nazwy zaczynają się najczęściej od litery P
.
Dla nazw instrukcji działających na liczbach zmiennoprzecinkowych nie wprowadzono żadnego prefiksu; jednak podobnie jak w MMX sufiks nazwy określa typ:
PS
(packed single) – działanie na wektorach,SS
(scalar single) – działanie na skalarach.
Ponadto jeśli rozkazy działają na połówkach rejestrów XMM (tj. albo odnoszą się do bitów 0..63, albo 64..127), w mnemonikach rozkazu występuje litera – odpowiednio – L
albo H
, od angielskich słów low („niski”) i high („wysoki”).
Działania zmiennoprzecinkowe
SSE jest zgodne ze standardem IEEE-754. Możliwe jest jednak włączenie pewnych niestandardowych cech, które w niektórych przypadkach przyspieszają obliczenia.
Działania arytmetyczne
- dodawanie (
ADDPS
,ADDSS
) - odejmowanie (
SUBPS
,SUBSS
) - mnożenie (
MULPS
,MULSS
) - dzielenie (
DIVPS
,DIVSS
) - przybliżenie odwrotności (1/x) (
RCPPS
,RCPSS
) - pierwiastek kwadratowy (
SQRTPS
,SQRTSS
) - przybliżenie odwrotności pierwiastka kwadratowego (
RSQRTPS
,RSQRTSS
) - wyznaczenie minimalnej wartości (
MINPS
,MINSS
) - wyznaczenie maksymalnej wartości (
MAXPS
,MAXSS
)
Działania logiczne
Działania logiczne są wykonywane na poziomie bitów, nie na liczbach zmiennoprzecinkowych:
- suma (
ORPS
); - iloczyn (
ANDPS
); - iloczyn z negacją (
ANDNPS
) – jeden z operandów jest negowany przed obliczeniem iloczynu; - różnica symetryczna (
XORPS
).
Porównania
Porównania w SSE są dwojakiego rodzaju:
- Modyfikujące rejestr SSE w sposób analogiczny jak w MMX
- Modyfikujące rejestr flag
Ad. 1. Modyfikujące rejestr SSE w sposób analogiczny jak w MMX: dla tych elementów, dla których wynik porównania jest prawdziwy wszystkie bity w rejestrze docelowym są ustawiane, gdy nieprawdziwy – zerowane. Ten sposób porównania może być zastosowany zarówno dla wektorów (rozkaz CMPPS
), jak i skalarów (rozkaz CMPSS
).
Przykład testowania, czy liczby są różne (rozkaz CMPNEQPS xmm0, xmm1
[1]):
+---------+---------+---------+---------+ | 1.0 | -5.3 | 16.5 | 17.2 | xmm0 +---------+---------+---------+---------+ ≠ ≠ ≠ ≠ +---------+---------+---------+---------+ | 7.0 | -5.3 | 16.5 | 17.3 | xmm1 +---------+---------+---------+---------+ = = = = +---------+---------+---------+---------+ |111..1111|000..0000|000..0000|111..1111| xmm0 +---------+---------+---------+---------+
Można testować 8 różnych relacji:
- równy,
- mniejszy,
- mniejszy lub równy,
- różny,
- większy,
- większy lub równy,
- ang. unordered[2].
- ang. orderded (odwrotność unordered).
Ad. 2. Modyfikujące rejestr flag, dzięki czemu można sterować przepływem sterowania za pomocą rozkazów skoku warunkowego; rozpoznawane są 4 różne relacje liczb: mniejszy, większy, równy oraz unordered. Ten sposób porównania może być stosowany tylko do skalarów, przy czym dostępne są dwa rozkazy: COMISS
– sygnalizujący błąd gdy wystąpi nieprawidłowa liczba zmiennoprzecinkowa QNaN lub SNaN oraz UCOMISS
– sygnalizująca błąd tylko w przypadku SNaN.
Konwersje pomiędzy liczbami całkowitymi i zmiennoprzecinkowymi
# | typ źródłowy | typ docelowy | instrukcja |
---|---|---|---|
1 | liczba całkowita | liczba zmiennoprzecinkowa | CVTSI2SS |
2 | para liczb całkowitych | para liczb zmiennoprzecinkowych | CVTPI2PS |
3 | liczba zmiennoprzecinkowa | liczba całkowita | CVTSS2SI |
4 | CVTTPS2PI | ||
5 | para liczb zmiennoprzecinkowych | para liczb całkowitych | CVTPS2PI |
6 | CVTTPS2PI |
Uwagi:
- Liczby całkowita, tj. 32-bitowe liczby ze znakiem
- Metoda zaokrąglania w większości rozkazów jest ustawiana w rejestrze kontrolnym
MXCSR
, wyjątkiem są rozkazyCVTTPS2PI
iCVTTSS2SI
dla których zawsze trybem zaokrąglanie jest ucinanie (ang. chop, truncate). - Wyniki zapisywane są do najmłodszego albo dwóch najmłodszych elementów wektora docelowego, zaś pozostałe elementy nie są zmieniane.
Rozmieszczenie elementów w wektorze
Rozkazy SHUFPS
, UNPCKLPS
, UNPCKHPS
umożliwiają różnorakie rozmieszczenie („wymieszanie”) elementów, np. odwrócenie kolejności elementów w wektorze.
UNPCKLPS, UNPCKHPS
Rozkazy ustawia na przemian 2 elementy z obu wektorów. UNPCKLPS
bierze dwa młodsze elementy (tj. o indeksach 0 i 1), natomiast UNPCKHPS
dwa starsze (indeksy 2 i 3). Rozkaz UNPCKLPS xmm1, xmm2
wykonuje:
temp[0] := xmm1[0] temp[1] := xmm2[0] temp[2] := xmm1[1] temp[3] := xmm2[1] xmm1 := temp
zaś UNPCKHPS xmm1, xmm2
temp[0] := xmm1[2] temp[1] := xmm2[2] temp[2] := xmm1[3] temp[3] := xmm2[3] xmm1 := temp
Np.
3 2 1 0 +-----+-----+-----+-----+ xmm1 = | d | c | b | a | +-----+-----+-----+-----+ +-----+-----+-----+-----+ xmm2 = | h | g | f | e | +-----+-----+-----+-----+
Wynik UNPCKLPS
:
+-----+-----+-----+-----+ xmm1 = | f | b | e | a | +-----+-----+-----+-----+
Wynik UNPCKHPS
:
+-----+-----+-----+-----+ xmm1 = | h | d | g | c | +-----+-----+-----+-----+
SHUFPS
Rozkaz bardziej ogólny niż UNPCKxPS
, umożliwia wskazanie dowolnych indeksów z wektorów źródłowych za pomocą trzeciego argumentu (stałej natychmiastowej), w którym na każdych kolejnych dwóch bitach zapisane są 4 indeksy. Rozkazowi SHUFPS xmm1, xmm2, imm8
odpowiada:
{ pobranie indeksów } index1_0 := (imm8 AND 00000011b) index1_1 := (imm8 AND 00001100b) SHR 2 index2_0 := (imm8 AND 00110000b) SHR 4 index2_1 := (imm8 AND 11000000b) SHR 6 { rozmieszczenie elementów } temp[0] := xmm1[index1_0] temp[1] := xmm1[index1_1] temp[2] := xmm2[index2_0] temp[3] := xmm2[index2_1] xmm1 := temp
MXCSR – rejestr kontrolny/statusu
Rejestr MXCSR przechowuje:
- Ustawienia operacji zmiennoprzecinkowych:
- sposób zaokrąglanie wyniku:
- do najbliższej liczby całkowitej
- zaokrąglanie w stronę plus nieskończoności
- zaokrąglanie w stronę minus nieskończoności
- ucinanie (zaokrąglanie w stronę zera)
- flaga flush-to-zero – jeśli ustawiona w przypadku niedomiaru zamiast zgłaszania wyjątku, zapisywana jest liczba zero; działanie nie jest zgodne ze standardem, ale powoduje przyspieszenie programów
- sposób zaokrąglanie wyniku:
- Maski włączające zgłaszanie wyjątków przy błędach; wykrywane błędy:
- niewłaściwe argumenty (np. pierwiastkowanie ujemnej liczby),
- dzielenie przez zero,
- nadmiar (wynik jest zbyt duży),
- niedomiar (wynikiem jest liczba nie znormalizowana),
- niedokładny wynik (wynik nie może być dokładnie reprezentowany).
- Flagi wskazujące rodzaj błędu – ustawiane automatycznie przez procesor, niezależnie od tego, czy dany błąd jest zgłaszany, czy nie; muszą zostać wyzerowane programowo (zwykle w procedurze obsługi wyjątków SSE).
Przesłania liczb zmiennoprzecinkowych między rejestrami i pamięcią
Rozkazy działają na wektorach liczb zmiennoprzecinkowych (4 elementy).
MOVAPS, MOVUPS
Przesłanie 4 liczb zmiennoprzecinkowych pomiędzy rejestrem XMM a pamięcią lub innym rejestrem XMM:
MOVAPS
– wymaga, by adres pamięci był wyrównany do granicy 16 bajtów, tj. jego 4 najmłodsze bity muszą być równe zero – w przeciwnym przypadku zgłaszany jest błąd.MOVUPS
– nie nakłada takich ograniczeń, ale odczyt danych niewyrównanych jest zwykle wolniejszy.
MOVSS
Przesłanie jednej liczby zmiennoprzecinkowej pomiędzy rejestrem XMM a pamięcią lub innym rejestrem XMM. Rozkaz działa na elemencie 0 rejestrów XMM: przy przesłaniach z rejestru do rejestru tylko on jest zmieniany, przy przesłaniu z pamięci do rejestru zerowane są pozostałe elementy.
MOVLPS, MOVHPS
Przesłanie 2 liczb zmiennoprzecinkowych pomiędzy rejestrem XMM i pamięcią. Rozkaz MOVLPS
działa na elementach 0 i 1 rejestru XMM, natomiast MOVHPS
na elementach 2 i 3.
MOVLHPS, MOVHLPS
Przesłanie między rejestrami 64-bitów (2 liczb zmiennoprzecinkowych)
MOVLHPS
– zapisanie elementów 0 i 1 rejestru źródłowego na pozycjach 2 i 3 rejestru docelowego;MOVHLPS
– zapisanie elementów 2 i 3 rejestru źródłowego na pozycjach 0 i 1 rejestru docelowego.
MOVMSKPS
Utworzenie 4-bitowej maski z najstarszych bitów każdej z liczb (tj. z bitów znaku) i zapisanie jej do rejestru ogólnego przeznaczenia x86 (EAX
, EBX
itd.).
Pamięć podręczna
W SSE dostępne są trzy grupy rozkazów odnoszące się do pamięci podręcznej.
Pobranie danych „bliżej” procesora
Rozkazy PREFETCH
(PREFETCHT0
, PREFETCHT1
, PREFETCHT2
, PREFETCHNTA
) są rodzajami podpowiedzi (ang. hint) dla procesora, wskazującymi, że obszar pamięci powinien znaleźć się wyżej w hierarchii pamięci podręcznej: poziom 1 znajduje się najbliżej procesora, poziom 2 dalej itd. Im „bliżej” procesora znajdują się dane, tym mniejszy jest czas oczekiwania na nie; np. jeśli dane są już w pamięci podręcznej pierwszego poziomu, prawie w ogóle nie trzeba czekać, w przeciwnym razie czas oczekiwania może wynieść kilkanaście, a nawet kilkadziesiąt lub kilkaset cykli maszynowych[3].
Programista czy kompilator wie lepiej kiedy i które dane wykorzystuje – za pomocą rozkazów PREFETCH
może odpowiednio wcześniej powiadomić procesor o zapotrzebowaniu i uniknąć tym samym oczekiwania, kiedy dane będą już potrzebne.
Należy zauważyć, że procesor może owych „podpowiedzi” w ogóle nie uwzględnić.
Trwały zapis (ang. non-temporal)
Pamięć podręczna służy m.in. do szybkiego sięgania po ostatnio zapisane informacje. Jednak zauważono, że pewnych przypadkach dane zapisywane z rejestrów do pamięci nie są więcej używane i dlatego nie ma potrzeby, aby zapisywać je w pamięci podręcznej (oraz marnować przy okazji jej ograniczone zasoby).
W SSE wprowadzono trzy rozkazy pozwalające przesłać dane do pamięci z pominięciem pamięci podręcznej:
MOVNTQ
– zapis zawartości rejestru MMXMOVNTPS
– zapis zawartości rejestru SSEMASKMOVQ
– zapis wybranych bajtów z rejestru MMX
W przypadku innych przesłań do pamięci, procesor może zakładać, że wszystkie zapisy do pamięci są tymczasowe (ang. temporal) i w ogóle nie uaktualniać zawartości pamięci głównej. Za pomocą rozkazu SFENCE
wymusza się synchronizację – patrz bariera pamięci.
64-bitowe rozkazy całkowitoliczbowe
Operandami dodatkowych rozkazów całkowitoliczbowych są tylko rejestry MMX; w SSE2 pojawiła się już możliwość wykorzystania również rejestrów SSE.
Rozkazy:
PAVGB
,PAVGW
– średnia bajtów/słów bez znaku (tutaj: słowo ma 16-bitów)PMAXUB
,PMINUB
– wybranie bajtów bez znaku o maksymalnej/minimalnej wartościPMAXSW
,PMINSW
– wybranie słów ze znakiem o maksymalnej/minimalnej wartościPMOVMSKB
– utworzenie maski bitowej z najstarszych bitów wszystkich bajtówPMULHUW
– starsze słowo z wyniku mnożenia słów bez znakuPSADBW
– suma modułów różnicy bajtów (tj. czyli odległość w metryce manhattan)PEXTRW
,PINSRW
– pobranie/wstawienie dowolnego słowa wektoraPSHUFW
– rozmieszczenie słów w wektorze
Rozszerzenia SSE
Kolejne rozszerzenia do zestawu instrukcji SSE:
- SSE2 – 2000 rok (wprowadzone przez firmę Intel):
- wprowadzenie działań wektorowych i skalarnych na liczbach zmiennoprzecinkowych podwójnej precyzji,
- umożliwienie wykonywania działań całkowitoliczbowych na 128-bitowych rejestrach XMM,
- większa kontrola nad pamięcią podręczną.
- SSE3 – 2004 rok (Intel):
- dodatkowe rozkazy wektorowe działające na liczbach zmiennoprzecinkowych pojedynczej i podwójnej precyzji,
- sprzętowe wspomaganie synchronizacji wątków.
- SSSE3 – 2006 rok (Intel):
- dodatkowe rozkazy wektorowe działające na liczbach całkowitych,
- rozkaz umożliwiający wyznaczenie zadanej permutacji bajtów w rejestrze XMM.
- SSE4 – 2007 rok (Intel):
- dodatkowe rozkazy wektorowe działające zarówno na liczbach całkowitych jak zmiennoprzecinkowych,
- rozkazy wektorowe wspomagające kompresję wideo,
- rozkazy wektorowe wykonujące działania na łańcuchach znaków,
- rozkaz wyznaczający sumę CRC-32.
- SSE5 – 2009 rok (AMD):
- dodatkowe rozkazy wektorowe działające zarówno na liczbach całkowitych jak zmiennoprzecinkowych,
- wprowadzenie rozkazów trój- i czteroargumentowych, w który jeden z argumentów jest docelowy (rozwiązanie z architektury RISC) – dotychczas rozkazy były 2-argumentowe, z czego jeden był równocześnie docelowy i jeśli jego wartość była potrzebna w dalszej części obliczeń, należało go zapamiętać – w rozkazach 3- i 4-argumentowych takiego problemu nie ma,
- rozkazy 4-argumentowe pozwalają akumulować wyniki mnożenia według schematu
- Advanced Vector Extensions – 2010 rok (Intel):
- dodanie nowych, 256-bitowych rejestrów: część istniejących rozkazów SSE, SSE2, SSE3 i SSSE3, głównie zmiennoprzecinkowych może wykonywać działania na tych rejestrach,
- kilka rozkazów wektorowych działających wyłącznie na 256-bitowych rejestrach,
- część istniejących rozkazów może być wykonywana wariantach 3-argumentowych (jak w SSE5),
- rozkazy 4-argumentowe pozwalają akumulować wyniki mnożenia na liczbach zmiennoprzecinkowych według schematu
- zwiększono z 8 do 32 liczbę relacji, które można sprawdzić rozkazami porównania (
CMPPS
,CMPPD
), - sprzętowe wsparcie szyfrowania AES.
Zobacz też
Przypisy
- ↑ Rozkazy
CMPPS
/CMPSS
są trójargumentowe: dwa pierwsze argumenty to porównywane wektory, trzeci argument to stała określająca testowaną relację. Jednak Intel proponuje, aby w asemblerze dostępne były dwurgumentowe pseduorozkazy, w których rodzaj relacji zapisany będzie w mnemoniku – np.CMPNEQPS xmm0, xmm1
odpowiadaCMPPS xmm0, xmm1, 4
(4 – kod dla relacji „różny”, ang. Not EQual). - ↑ Relacja unordered jest prawdziwa, gdy argumentów nie można porównać, ponieważ przynajmniej jeden z nich jest nie-liczbą (NaN) lub nie reprezentuje prawidłowej liczby zmiennoprzecinkowej.
- ↑ Orientacyjne wartości: jeśli dane są w L1 – 2-3 cykle, w L2 – rzędu 10 cykli, w pamięci głównej – rzędu 100–200 cykli.