Korzystanie z instrukcji SSE

Mam pętlę napisaną w C++, która jest wykonywana dla każdego elementu Wielkiej tablicy liczb całkowitych. Wewnątrz pętli maskuję kilka bitów liczby całkowitej, a następnie znajduję wartości min i max. Słyszałem, że jeśli użyję instrukcji SSE do tych operacji, będzie to działać znacznie szybciej w porównaniu do normalnej pętli napisanej przy użyciu bitowych warunków and , oraz if-else. Moje pytanie brzmi, Czy powinienem przejść do tych instrukcji SSE? Co się stanie, jeśli mój kod będzie działał na innym procesorze? Czy nadal będzie działać lub te instrukcje są specyficzne dla procesora?

Author: Naveen, 2009-02-25

15 answers

  1. instrukcje SSE są specyficzne dla procesora. Możesz sprawdzić, który procesor obsługuje jaką wersję SSE na Wikipedii.
  2. to, czy kod SSE będzie szybszy lub nie zależy od wielu czynników: pierwszym jest oczywiście to, czy problem jest związany z pamięcią czy CPU. Jeśli szyna pamięci jest wąskim gardłem SSE niewiele pomoże. Spróbuj uprościć obliczenia liczbowe, jeśli to przyspieszy kod, prawdopodobnie jest związany z CPU i masz duże szanse na przyspieszenie go.
  3. Należy pamiętać, że pisanie kodu SIMD jest o wiele trudniejsze niż pisanie kodu C++, a wynikowy kod jest o wiele trudniejszy do zmiany. Zawsze aktualizuj kod C++, będziesz chciał go jako komentarz i sprawdzić poprawność kodu asemblera.
  4. pomyśl o użyciu biblioteki takiej jak IPP, która implementuje typowe niskopoziomowe operacje SIMD zoptymalizowane dla różnych procesorów.
 24
Author: Niki,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-02-25 16:09:32

SIMD, którego przykładem jest SSE, pozwala wykonać tę samą operację na wielu fragmentach danych. Tak więc, nie otrzymasz żadnej korzyści z używania SSE jako prostego zamiennika dla operacji integer, otrzymasz korzyści tylko wtedy, gdy możesz wykonywać operacje na wielu elementach danych naraz. Polega to na załadowaniu niektórych wartości danych, które są sąsiadujące ze sobą w pamięci, wykonaniu wymaganego przetwarzania, a następnie przejściu do następnego zestawu wartości w tablicy.

Problemy:

1 Jeśli kod ścieżka jest zależna od przetwarzanych danych, SIMD staje się znacznie trudniejsze do wdrożenia. Na przykład:

a = array [index];
a &= mask;
a >>= shift;
if (a < somevalue)
{
  a += 2;
  array [index] = a;
}
++index;

Nie jest łatwo zrobić jako SIMD:

a1 = array [index] a2 = array [index+1] a3 = array [index+2] a4 = array [index+3]
a1 &= mask         a2 &= mask           a3 &= mask           a4 &= mask
a1 >>= shift       a2 >>= shift         a3 >>= shift         a4 >>= shift
if (a1<somevalue)  if (a2<somevalue)    if (a3<somevalue)    if (a4<somevalue)
  // help! can't conditionally perform this on each column, all columns must do the same thing
index += 4

2 Jeśli dane nie są ze sobą powiązane, ładowanie danych do instrukcji SIMD jest kłopotliwe

3 kod jest specyficzny dla procesora. SSE jest tylko na IA32 (Intel / AMD) i nie wszystkie procesory IA32 obsługują SSE.

Musisz przeanalizować algorytm i dane, aby zobaczyć, czy może być SSE ' D, A to wymaga wiedzy, jak działa SSE. Na stronie Intela jest mnóstwo dokumentacji.

 16
Author: Skizz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-03 14:03:15

Ten rodzaj problemu jest doskonałym przykładem, gdzie dobry profiler niskiego poziomu jest niezbędny. (Coś jak VTune) może dać ci znacznie bardziej świadomy pomysł, gdzie leżą Twoje hotspoty.

Domyślam się, że z tego, co opisujesz, Twój hotspot prawdopodobnie będzie awarią przewidywania gałęzi wynikającą z min/max obliczeń za pomocą if/else. Dlatego korzystanie z SIMD intrinsics powinno pozwolić na korzystanie z instrukcji min / max, jednak może warto po prostu spróbować użyć rozgałęzienia zamiast tego kalukulacja min / max. To może osiągnąć większość zysków z mniej bólu.

Coś takiego:

inline int 
minimum(int a, int b)
{
  int mask = (a - b) >> 31;
  return ((a & mask) | (b & ~mask));
}
 10
Author: Peter Jeffery,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-02-27 21:09:56

Jeśli używasz instrukcji SSE, jesteś oczywiście ograniczony do procesorów, które je obsługują. Oznacza to, że x86 pochodzi z Pentium 2 lub tak (nie pamiętam dokładnie, kiedy zostały wprowadzone, ale to dawno temu)

SSE2, który, o ile dobrze pamiętam, jest Tym, który oferuje operacje integer, jest nieco nowszy (Pentium 3? Chociaż pierwsze procesory AMD Athlon nie obsługują ich)

W każdym razie, masz dwie opcje korzystania z tych instrukcji. Albo napisz cały blok kodu w assembly (prawdopodobnie zły pomysł. To praktycznie uniemożliwia kompilatorowi optymalizację kodu, a człowiekowi bardzo trudno jest napisać wydajny asembler).

Alternatywnie, użyj intrinsików dostępnych z kompilatorem (jeśli pamięć służy, są one zwykle zdefiniowane w xmmintrin.h)

Ale ponownie, wydajność może nie poprawić. Kod SSE stawia dodatkowe wymagania dotyczące przetwarzanych danych. Przede wszystkim należy pamiętać, że dane muszą być wyrównane na granicach 128-bitowych. Pomiędzy wartościami załadowanymi do tego samego rejestru powinno być niewiele lub nie powinno być żadnych zależności (128-bitowy rejestr SSE może zawierać 4 int. Dodanie pierwszego i drugiego razem nie jest optymalne. Ale dodanie wszystkich czterech Int do odpowiednich 4 int w innym rejestrze będzie szybkie)

Może być kuszące Użycie biblioteki, która owija wszystkie niskopoziomowe modyfikacje SSE, ale może to również zrujnować potencjalne korzyści z wydajności.

I don ' t wiedzieć, jak dobra jest obsługa operacji integer SSE, więc może to być również czynnik, który może ograniczyć wydajność. SSE ma na celu przede wszystkim przyspieszenie operacji zmiennoprzecinkowych.

 6
Author: jalf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-02-25 16:15:15

Jeśli zamierzasz używać Microsoft Visual C++, powinieneś przeczytać to:

Http://www.codeproject.com/KB/recipes/sseintro.aspx

 4
Author: Migol,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-02-25 16:19:03

Zaimplementowaliśmy kod przetwarzania obrazu, podobny do tego, co opisujesz, ale na tablicy bajtów, w SSE. Szybkość w porównaniu do kodu C jest znaczna, w zależności od dokładnego algorytmu więcej niż współczynnik 4, nawet w odniesieniu do kompilatora Intela. Jednak, jak już wspomniałeś, masz następujące wady:

  • Przenośność. Kod będzie działał na każdym procesorze Intel-like, więc także AMD, ale nie na innych procesorach. To nie jest dla nas problemem, ponieważ kontrolujemy cel sprzęt. Problemem może być również przełączenie kompilatorów, a nawet na 64-bitowy system operacyjny.

  • Masz stromą krzywą uczenia się, ale odkryłem, że po pojęciu zasad pisanie nowych algorytmów nie jest takie trudne.

  • Konserwacja. Większość programistów C lub c++ nie zna assembly/SSE.

Moja rada dla ciebie będzie iść na to tylko wtedy, gdy naprawdę potrzebujesz poprawy wydajności, a nie można znaleźć funkcji dla swojego problemu w bibliotece jak Intel IPP, i jeśli możesz żyć z problemami z przenośnością.

 3
Author: Dani van der Meer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-02-25 16:16:19

Mogę powiedzieć z mojego experince, że SSE przynosi ogromne (4X i więcej) przyspieszenie nad zwykłą wersją kodu c (bez wbudowanego asm, bez wewnętrznych elementów), ale ręcznie zoptymalizowany asembler może pokonać kompilator generowany przez kompilator, jeśli kompilator nie może zrozumieć, co zamierzał programista(uwierz mi, Kompilatory nie obejmują wszystkich możliwych kombinacji kodu i nigdy nie będą). AHA i kompilator nie może za każdym razem układać danych, które uruchamia z najszybszą możliwą prędkością. Ale trzeba dużo experince dla przyspieszenie przez kompilator Intela (jeśli to możliwe).

 3
Author: Quonux,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-07-10 22:11:28

Instrukcje SSE były pierwotnie tylko na chipach Intela, ale ostatnio (od Athlona?) AMD również je obsługuje, więc jeśli wykonujesz kod zgodnie z zestawem instrukcji SSE, powinieneś być przenośny do większości procków x86.

To powiedziawszy, może nie być warte twojego czasu, aby nauczyć się kodowania SSE, chyba że znasz już asemblera na x86 - łatwiejszą opcją może być sprawdzenie dokumentów kompilatora i sprawdzenie, czy istnieją opcje pozwalające kompilatorowi na autogenerację kodu SSE dla Ciebie. Niektóre Kompilatory zrobić bardzo dobrze pętle wektoryzacji w ten sposób. (Prawdopodobnie nie dziwi cię to, że Kompilatory Intela wykonują dobrą robotę:)

 2
Author: Mike,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-02-25 16:12:09

Napisz kod, który pomoże kompilatorowi zrozumieć, co robisz. GCC zrozumie i zoptymalizuje kod SSE taki jak:

typedef union Vector4f
{
        // Easy constructor, defaulted to black/0 vector
    Vector4f(float a = 0, float b = 0, float c = 0, float d = 1.0f):
        X(a), Y(b), Z(c), W(d) { }

        // Cast operator, for []
    inline operator float* ()
    { 
        return (float*)this;
    }

        // Const ast operator, for const []
    inline operator const float* () const
    { 
        return (const float*)this;
    }

    // ---------------------------------------- //

    inline Vector4f operator += (const Vector4f &v)
    {
        for(int i=0; i<4; ++i)
            (*this)[i] += v[i];

        return *this;
    }

    inline Vector4f operator += (float t)
    {
        for(int i=0; i<4; ++i)
            (*this)[i] += t;

        return *this;
    }

        // Vertex / Vector 
        // Lower case xyzw components
    struct {
        float x, y, z;
        float w;
    };

        // Upper case XYZW components
    struct {
        float X, Y, Z;
        float W;
    };
};

Tylko nie zapomnij mieć-msse-msse2 na parametrach kompilacji!

 2
Author: LiraNuna,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-02-27 08:44:16

Chociaż prawdą jest, że SSE jest specyficzne dla niektórych procesorów (SSE może być stosunkowo bezpieczny, SSE2 znacznie mniej z mojego doświadczenia), można wykryć procesor w czasie wykonywania i załadować kod dynamicznie w zależności od procesora docelowego.

 1
Author: David Cournapeau,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-02-25 16:31:16

Intrinsics SIMD (takie jak SSE2) może przyspieszyć tego typu rzeczy, ale wymaga wiedzy, aby poprawnie używać. Są one bardzo wrażliwe na wyrównanie i opóźnienia rurociągów; nieostrożne użycie może sprawić, że wydajność będzie jeszcze gorsza, niż byłoby bez nich. Uzyskasz znacznie łatwiejsze i bardziej natychmiastowe przyspieszenie od prostego użycia wstępnego ustawiania pamięci podręcznej, aby upewnić się, że wszystkie Twoje ints są w L1 na czas, abyś mógł na nich operować.

Chyba że twoja funkcja potrzebuje przepustowości lepszej niż 100 000 000 liczb całkowitych na sekundę, SIMD prawdopodobnie nie jest warte kłopotu dla Ciebie.

 1
Author: Crashworks,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-02-26 08:43:04

Dodam krótko to, co zostało powiedziane wcześniej o różnych wersjach SSE dostępnych na różnych procesorach: można to sprawdzić, patrząc na odpowiednie flagi funkcji zwracane przez instrukcję CPUID(patrz np. dokumentacja Intela).

 1
Author: PhiS,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-02-26 11:49:12

Spójrz na inline assembler dla C/C++, oto DDJ Artykuł . Jeśli nie masz 100% pewności, że twój program będzie działał na kompatybilnej platformie, powinieneś postępować zgodnie z zaleceniami, które podało tu wiele osób.

 1
Author: epatel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-02-26 12:01:51

Zgadzam się z poprzednimi plakatami. Korzyści mogą być dość duże, ale ich uzyskanie może wymagać dużo pracy. Dokumentacja Intela na tych instrukcjach zawiera ponad 4K stron. Możesz sprawdzić easysse (C++ wrappers library over intrinsics + examples) za darmo z Ocali Inc.

Zakładam, że moja przynależność do tego EasySSE jest jasna.

 1
Author: Ogan Ocali,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-29 20:07:09

Nie polecam robić tego samemu, chyba że jesteś dość biegły w montażu. Korzystanie z SSE będzie bardziej niż prawdopodobne, wymagać starannej reorganizacji danych, jak wskazuje Skizz, a korzyści są często wątpliwe w najlepszym przypadku.

Byłoby prawdopodobnie dużo lepiej dla ciebie pisać bardzo małe pętle i trzymać dane bardzo ściśle zorganizowane i po prostu polegać na kompilatorze robi to za Ciebie. Zarówno kompilator Intel C, jak i GCC (od 4.1) mogą automatycznie wektoryzować kod, a pewnie zrobi to lepiej niż ty. (Wystarczy dodać -ftree-vectorize do swojego CXXFLAGS.)

Edit : kolejną rzeczą, którą powinienem wspomnieć, jest to, że kilka kompilatorów wspiera Assembly intrinsics , które prawdopodobnie byłyby, IMO, łatwiejsze w użyciu niż składnia asm() lub __asm {}.

 0
Author: greyfade,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:54:15