Erste Schritte mit SSE3 und AVX

Was unterstützt mein Rechner?

Mit

$shell> curl -s0 http://repnop.org/releases/cpuid-0.2.tar.gz | tar xz && (mv cpuid-0.2 cpuid && cd cpuid && make )                            
gcc -I. -std=c99 -pedantic -Wall -W -Wundef -Wendif-labels -Wshadow -Wpointer-arith -Wbad-function-cast -Wcast-align -Wwrite-strings -Wstrict-prototypes -Wmissing-prototypes -Wnested-externs -Winline -Wdisabled-optimization -fstrict-aliasing -O2 -pipe -Wno-parentheses -o cpuid cpuid.c tool.c

lädt und übersetzt man ein kleines Tool mit dem man dann die Hardware abfragen kann:

$shell> cd cpuid                                                            
$shell> ./cpuid -b                                                          
Intel(R) Core(TM)2 Duo CPU     P8600  @ 2.40GHz
$shell> ./cpuid -f                                                          
FPU VME DE PSE TSC MSR PAE MCE CX8 APIC SEP MTRR PGE MCA CMOV PAT PSE36 CLFSH 
DS ACPI MMX FXSR SSE SSE2 SS HTT TM PBE SSE3 DTES64 MONITOR DSCPL VMX SMX EST 
TM2 SSSE3 CMPXCHG16B XTPR PDCM SSE4.1 NX SYSCALL X86_64 

Einfaches Beispiel mit SSE3

#include <stdio.h>
#include <emmintrin.h>

double x[4] __attribute__ ((aligned (16))) = {1.02.03.04.0};
double y[5] __attribute__ ((aligned (16))) = {1.02.03.04.0};

void
foo(void)
{
    __m128d x01 = _mm_load_pd(x);
    __m128d x23 = _mm_load_pd(x+2);

    __m128d y01 = _mm_load_pd(y);
    __m128d y23 = _mm_load_pd(y+2);

    y01 = _mm_add_pd(x01, y01);
    y23 = _mm_add_pd(x23, y23);

    _mm_store_pd(y+1, y01);
    _mm_store_pd(y+3, y23);
}

int
main()
{
    foo();
    printf("y = (%lf%lf%lf%lf)\n", y[0], y[1], y[2], y[3]);
}

Aufgaben:

Einfaches Beispiel mit AVX

Ändert das Beispiel von oben so ab, dass AVX benutzt wird.

BLAS Level 1 mit SSE3 und AVX

Baut in einer BLAS Level 1 Routine eurer Wahl eine SSE3 oder AVX Variante ein. Welche Variante benutzt wird soll über ein Macro gesteuert werden:

#if defined(WITH_SSE)

// SSE Variante

#elif defined (WITH AVX)

// SSE Variante

# else

// Referenzimplementierung

#endif

Bemerkungen: - Das geht nur in den Fällen in denen alle Vektor-Inkremente Eins sind. - Bei daxpy müssen zum Beispiel beide Vektoren aligned sein oder keiner. - Natürlich sollt ihr das mit der Test- und Benchmark-Suite testen. - Und die Benchmarks sollte auch mit Gnuplot visualisiert werden.

Beispiel mit nicht-aligned Vektoren

#include <stdio.h>
#include <emmintrin.h>

double x[5] = {1.02.03.04.04.0};
double y[5] = {1.02.03.04.04.0};

void
foo(void)
{
    __m128d x01 = _mm_load_pd(x+1);
    __m128d x23 = _mm_load_pd(x+3);

    __m128d y01 = _mm_load_pd(y+1);
    __m128d y23 = _mm_load_pd(y+3);

    y01 = _mm_add_pd(x01, y01);
    y23 = _mm_add_pd(x23, y23);

    _mm_store_pd(y+1, y01);
    _mm_store_pd(y+3, y23);
}

int
main()
{
    foo();
    printf("y = (%lf%lf%lf%lf)\n", y[0], y[1], y[2], y[3], y[4]);
}