Streaming SIMD Extensions
Z Wikipedii
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.
Spis treści |
[edytuj] Typy danych
SSE wprowadza nowy typ danych: 4-elementowy wektor liczb zmiennoprzecinkowych pojedynczej precyzji (ang. 128-bit packed sigle-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 +-------+-------+-------+-------+
[edytuj] 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").
[edytuj] 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.
[edytuj] 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)
[edytuj] 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).
[edytuj] Porównania
Porównania w SSE są dwojakiego rodzaju:
- 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).
- 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.
[edytuj] 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ą rozkazy CVTTPS2PI i CVTTSS2SI 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.
[edytuj] 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.
[edytuj] 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ś UNPCKLPS 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 | +-----+-----+-----+-----+
[edytuj] 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
[edytuj] 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).
[edytuj] Przesłania liczb zmiennoprzecinkowych między rejestrami i pamięcią
Rozkazy działają na wektorach liczb zmiennoprzecinkowych (4 elementy).
[edytuj] 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.
[edytuj] 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.
[edytuj] MOVLSP, MOVHSP
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.
[edytuj] 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.
[edytuj] 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.).
[edytuj] Pamięć podręczna
W SSE dostępne są trzy grupy rozkazów odnoszące się do pamięci podręcznej.
[edytuj] 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ć.
[edytuj] 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 MMX
- MOVNTPS - zapis zawartości rejestru SSE
- MASKMOVQ - 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ę.
[edytuj] 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ści
- PMAXSW, PMAXSW - wybranie słów ze znakiem o maksymalnej/minimalnej wartości
- PMOVMSKB - utworzenie maski bitowej z najstarszych bitów wszystkich bajtów
- PMULHUW - starsze słowo z wyniku mnożenia słów bez znaku
- PSADBW - suma modułów różnicy bajtów (tj. , czyli odległość w metryce manhattan)
- PEXTRW, PINSRW - pobranie/wstawienie dowolnego słowa wektora
- PSHUFW - rozmieszczenie słów w wektorze
[edytuj] 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
- rozkazy wyznaczający sumę CRC32
- SSE5 - zapowiadane na 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-arguementowych takiego problemu nie ma
- rozkazy 4-argumentowe pozwalają akumulować wyniki mnożenia wg schematu
- Advanced Vector Extensions - zapowiadane na 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 wg schematu
- zwiększono z 8 do 32 liczbę relacji, które można sprawdzić rozkazami porównania (CMPPS, CMPPD)
- sprzętowe wsparcie szyfrowania AES
[edytuj] 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 odpowiada CMPPS 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.