Lekki compilator c++ + SSE

0

Potrzebuję zoptymalizować ekstremalnie obliczenia na double i nie widzę dobrych kompilatorów do tego.
Najlepiej żeby również pozwalał pisać w tych wstawkach asm instrukcje sse2, sse3, ...

Sprawdzałem gcc, i to strasznie marnie działa.
Bez SEE - tylko optymalizacja FPU - generuje sekwencyjny kod, jak kompletny przygłup.

Kiedyś używałem BC5 i tam była opcja: intel optimised compiler;
gdy się tego użyło, wówczas generował z 2-4 razy szybszy kod na floatach, a tu nic - gcc normalnie liczy jak bez jakiejkolwiek optymalizacji.

Np. gdy obliczam:

x = aaa;

r += b xc + ... // tu inne obliczenia

i ten głupek jedzie normalnie:

fld a ; ładuje a
fmul a ; zamiast fmul st0, ale to pryszcz...
fmul a ; od razu mnoży tak samo
fstp x ; x = a^3

To jest kompletne dno.

FPU może obliczać kilka operacji naraz - chyba do 4 mnożeń/dodawań, a wtedy wychodzi nawet 1 cykl na instrukcję, ale operacje muszą być niezależne od siebie, czyli tak jak gcc generuje nigdy nie należy robić.

Potem gcc + SEE3 i on to samo wygenerował, tylko pozmieniał mnemoniki rozkazów.
Powinien chyba próbować podwójnie trzaskać (tam było od groma operacji na tablicach z double), a nie wszystko skalarnie jak FPU.

0

A używasz natywnych typów takich jak __m128?

0

Słuchaj, a prefethujesz ? Jak nie umiesz tego robić, to są odpowiednie makra w gcc. Porozwijaj pętle i poumieszczaj iteracyjne zmienne w rejestrach to pomoże. Ja tak zrobiłem i skompilowałem bez optymalizacji to uzyskałem wydajność 800% większą niż kod pisany na pałe optymalizowany przez gcc -O3 .

0
lukasz1235 napisał(a)

A używasz natywnych typów takich jak __m128?

Tego nie sprawdzałem, to był zwyczajny c++, dla dowolnego kompilatora.

coś takiego:

struct TP3d
{       
  double x,y,z;

    void sub(TP3d &r) { x-=r.x; y -= r.y; z -= r.z; }
    void sub(TP3d &a, TP3d &b) { x = a.x-b.x; y = a.y-b.y; z = a.z-b.z; }

    void mul(double m) { x *= m; y *= m; z *= m; }
    void amul(TP3d &r, double m) { x += r.x*m; y += r.y*m; z += r.z*m; }
    void smul(TP3d &r, double m) { x -= r.x*m; y -= r.y*m; z -= r.z*m; }
};

Potem obliczam na takich współrzędnych różne rzeczy, np. mnożenie wektorów:

inline double operator*(TP3d &a, TP3d &b)
{ return a.x*b.x + a.y*b.y + a.z*b.z; }

albo bardzie złożone:

void force3(TP3d *a, TBody *y)
{
   // v' = sum mi * rij/rij^3
   // r' = v;

  TP3d r;
  double d;

  r.sub(y[0].r, y[1].r);
  d = r.x*r.x + r.y*r.y + r.z*r.z; d = 1/(d*sqrt(d));
  a[0].amul(r, y[1].m*d);
  a[1].smul(r, y[0].m*d);

  r.sub(y[0].r, y[2].r);
  d = r.x*r.x + r.y*r.y + r.z*r.z; d = 1/(d*sqrt(d));
  a[0].amul(r, y[2].m*d);
  a[2].smul(r, y[0].m*d);

  r.sub(y[1].r, y[2].r);
  d = r.x*r.x + r.y*r.y + r.z*r.z; d = 1/(d*sqrt(d));
  a[1].amul(r, y[2].m*d);
  a[2].smul(r, y[1].m*d);
}

po kompilacji do asm wychodzi ponad 200 instrukcji i kompletna porażka;
tworzy tu nawet jakieś zmienne pomocnicze na stosie - 240 bajtów, a te lokalne r i d to raptem 4*8 = 32B!

Nie wiem czy tu można użyć tego __m128... niby jak - jako wskaźnik do tablicy zamiast tych struktur?

0
lukas_gab napisał(a)

Słuchaj, a prefethujesz ?

Co mam prefechować?
Cache sam się załaduje, bo obliczam ciągle na jednej i raczej niedużej tablicy.

Zresztą po kodzie na FPU widać ewidentnie, że tam nie ma żadnej optymalizacji.

Jak nie umiesz tego robić, to są odpowiednie makra w gcc. Porozwijaj pętle i poumieszczaj iteracyjne zmienne w rejestrach to pomoże. Ja tak zrobiłem i skompilowałem bez optymalizacji to uzyskałem wydajność 800% większą niż kod pisany na pałe optymalizowany przez gcc -O3 .

Chodzi raczej o optymalizację z automatu. Nie mam w pamięci czasu wykonywania setek instrukcji.

A nawet gdyby, no to wolę bezpośrednio robić instrukcjami procesora, zamiast używać jakichś fikuśnych makr, które stworzono pewnie głównie z uwagi na przenośność kodu, bo przecież nie do kodowania operacji sse... innymi nazwami.

0

Googluj "sse intrinsics". Intel zrobił dobre manuale (w końcu to ich), na msdn też jest pomoc do tego.
Na double i tak nie zdziałasz cudów, bo do rejestrów XMM mieszczą się dwie takie liczby. Dopiero AVX mają 256 bitowe rejestry YMM na 4 double.

W GCC włącz -ftree-vectorize -fdump-tree-vect-details i zobacz co się stanie.

0

Kod z instrukcjami sse2 pójdzie wszędzie. To jest standardem już prawie 10lat.

Ale widzę, że w zasadzie nie ma kompilatorów optymalizujących kod.

Może ten Intela?
Można tam pisać instrukcje żywcem w asm zamiast tych introsików?

0

sprawdź kompilator intela icc, nie pamiętam dokładnie jak z jego licencją ale do domowego użytku jest darmowy i na procesorach intela optymalizacja działa bardzo dobrze:)

0

Jednak jest tam jakaś optymalizacja, ale trzeba używać typu _m128 (kompilator wyrównuje to zawsze do 16B).

należałoby tworzyć coś w tym stylu:

struct TP4m
{
  union {
    TP4d r;
    _m128 m[2];
   };
};

Potem nawet lokalne z automatu wyrównuje do 16:

void force3(TP4m *a, ...)
{
  TP4m r; // powinien skorygować odpowiednio stos

union {
  double s; // to stoi dobrze, ale pewne i tak trzeba to tak unifikować...
  __m128 bb;
  };

 ...
}

sieczka... jeszcze trochę i będzie łatwiej maszynowo programować.
Może tak:
emit(seria bajtów).

Można nawet przedwojennym kompilatorem tak programować...
ma ktoś tabelę opckodów sse?

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0