2017-11-04 3 views
1

Ich erwartete AVX etwa 1,5 mal schneller als SSE. Alle 3 Arrays (3 Arrays * 16384 Elemente * 4 Bytes/Element = 196608 Bytes) sollten in L2-Cache (256 KB) auf einer Intel Core-CPU (Broadwell) passen.AVX vs. SSE: erwarten Sie eine größere Beschleunigung

Gibt es spezielle Compiler-Direktiven oder Flags, die ich verwenden sollte?

Compiler Version

$ clang --version 
Apple LLVM version 9.0.0 (clang-900.0.38) 
Target: x86_64-apple-darwin16.7.0 
Thread model: posix 
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin 

Compile Linie

$ make avx 
clang -O3 -fno-tree-vectorize -msse -msse2 -msse3 -msse4.1 -mavx -mavx2 avx.c ; ./a.out 123 
n: 123 
    AVX Time taken: 0 seconds 177 milliseconds 
vector+vector:begin int: 1 5 127 0 

    SSE Time taken: 0 seconds 195 milliseconds 
vector+vector:begin int: 1 5 127 0 

avx.c

#include <stdio.h> 
#include <stdlib.h> 
#include <x86intrin.h> 
#include <time.h> 
#ifndef __cplusplus 
#include <stdalign.h> // C11 defines _Alignas(). This header defines alignas() 
#endif 
#define REPS 50000 
#define AR 16384 

// add int vectors via AVX 
__attribute__((noinline)) 
void add_iv_avx(__m256i *restrict a, __m256i *restrict b, __m256i *restrict out, int N) { 

    __m256i *x = __builtin_assume_aligned(a, 32); 
    __m256i *y = __builtin_assume_aligned(b, 32); 
    __m256i *z = __builtin_assume_aligned(out, 32); 

    const int loops = N/8; // 8 is number of int32 in __m256i 
    for(int i=0; i < loops; i++) { 
     _mm256_store_si256(&z[i], _mm256_add_epi32(x[i], y[i])); 
    } 
} 

// add int vectors via SSE; https://en.wikipedia.org/wiki/Restrict 
__attribute__((noinline)) 
void add_iv_sse(__m128i *restrict a, __m128i *restrict b, __m128i *restrict out, int N) { 

    __m128i *x = __builtin_assume_aligned(a, 16); 
    __m128i *y = __builtin_assume_aligned(b, 16); 
    __m128i *z = __builtin_assume_aligned(out, 16); 

    const int loops = N/sizeof(int); 
    for(int i=0; i < loops; i++) { 
     //out[i]= _mm_add_epi32(a[i], b[i]); // this also works 
     _mm_storeu_si128(&z[i], _mm_add_epi32(x[i], y[i])); 
    } 
} 

// printing 
void p128_as_int(__m128i in) { 
    alignas(16) uint32_t v[4]; 
    _mm_store_si128((__m128i*)v, in); 
    printf("int: %i %i %i %i\n", v[0], v[1], v[2], v[3]); 
} 

__attribute__((noinline)) 
void debug_print(int *h) { 
    printf("vector+vector:begin "); 
    p128_as_int(* (__m128i*) &h[0]); 
} 

int main(int argc, char *argv[]) { 
    int n = atoi (argv[1]); 
    printf("n: %d\n", n); 

    int *x,*y,*z; 
    if (posix_memalign((void**)&x, 32, 16384*sizeof(int))) { free(x); return EXIT_FAILURE; } 
    if (posix_memalign((void**)&y, 32, 16384*sizeof(int))) { free(y); return EXIT_FAILURE; } 
    if (posix_memalign((void**)&z, 32, 16384*sizeof(int))) { free(z); return EXIT_FAILURE; } 
    x[0]=0; x[1]=2; x[2]=4; 
    y[0]=1; y[1]=3; y[2]=n; 

    // touch each 4K page in x,y,z to avoid copy-on-write optimizations 
    for (int i=512; i<AR; i+= 512) { x[i]=1; y[i]=1; z[i]=1; } 

    // warmup 
    for(int i=0; i<REPS; ++i) { add_iv_avx((__m256i*)x, (__m256i*)y, (__m256i*)z, AR); } 
    // AVX 
    clock_t start = clock(); 
    for(int i=0; i<REPS; ++i) { add_iv_avx((__m256i*)x, (__m256i*)y, (__m256i*)z, AR); } 
    int msec = (clock()-start) * 1000/CLOCKS_PER_SEC; 
    printf(" AVX Time taken: %d seconds %d milliseconds\n", msec/1000, msec%1000); 
    debug_print(z); 

    // warmup 
    for(int i=0; i<REPS; ++i) { add_iv_sse((__m128i*)x, (__m128i*)y, (__m128i*)z, AR); } 
    // SSE 
    start = clock(); 
    for(int i=0; i<REPS; ++i) { add_iv_sse((__m128i*)x, (__m128i*)y, (__m128i*)z, AR); } 
    msec = (clock()-start) * 1000/CLOCKS_PER_SEC; 
    printf("\n SSE Time taken: %d seconds %d milliseconds\n", msec/1000, msec%1000); 
    debug_print(z); 

    return EXIT_SUCCESS; 
} 
+0

Wiederholen Sie es mehrmals in einer Schleife, vertauschen Sie die Reihenfolge zwischen SSE und AVX, etc. Am Ende, bekomme ich (auf Skylake) ein Verhältnis, das ziemlich nahe an der 1,5 ist, die Sie erwarten. –

+2

Für solch eine winzige Menge an Rechen pro Element (nur eine Addition) würde ich erwarten, dass die Ausführungszeit meistens speichergebunden ist. – EOF

+1

Sie sind auf Broadwell, also haben Sie nicht Skylakes Hardware-P-State-Feature für schnelles Hochfahren auf Max Turbo. 36ms ist sehr kurz für die Messung der Wanduhrzeit (anstelle von Kerntaktzyklen). Es gibt auch die AVX Aufwärmphase von ungefähr 14 us, wo 256b Befehle vielleicht 4x langsamer sind: http://www.agner.org/optimize/blog/read.php?i=415. Agner sagt, er habe es nicht vor dem Skylake beobachtet, aber andere haben es. Wie auch immer, wenn SSE zuerst ausgeführt wird, hat es einige Zeit mit niedriger Taktrate, es sei denn, du hast das zuerst durch Aufwärmen gesteuert. –

Antwort

3

Das Problem ist, dass t Hat Ihre Daten nicht in den L1-Cache passen. Die L1-Bandbreite von Broadwell ist viel größer als die L2-Bandbreite. Die L1-Bandbreite ist groß genug, um zwei 32-Byte-Vektoren pro CPU-Zyklus zu laden. Also, eine bessere AVX vs. SSE-Beschleunigung könnte erwartet werden, wenn Ihr Datensatz viel kleiner ist. Es sei jedoch angemerkt, dass die kombinierte L1-Lese/Schreib-Bandbreite weniger als 2 × 32 (r) +32 (w) = 96 Bytes pro Zyklus ist. In der Praxis sind 75 Bytes pro Zyklus möglich, siehe here.

Die zweite Kurve auf this Seite zeigt, dass in der Tat der L2-Bandbreite ist viel kleiner: Bei Test_block_size = 128 KB (= 32 KB pro Kern) die Bandbreite 900GB/s ist. Bei Test_block_size = 1 MB (= 256 KB pro Kern) ist die Bandbreite nur 300 GB/s. (Beachten Sie, dass Haswell 4770k hat mehr oder weniger die gleichen L1- und L2-Cache-Architektur als Broadwell.)

Versuchen AR-2.000 zu reduzieren und NREP bis 1000000 zu erhöhen und sehen, was mit dem SSE vs. AVX Speedup passiert.

+2

Es kommt mir vor, dass Gcc-Loops wahrscheinlich auch Front-End-Engpässe sind. Obwohl dieser Effekt zugunsten von AVX2 wäre, weil es zweifach ist, die Arbeit pro UP zu machen. –

+0

@PeterCordes Ja, das könnte hier auch eine Rolle spielen. Die Reduzierung der Arraygröße 'AR' auf 2000 führte erwartungsgemäß zu einer AVX vs. SSE Beschleunigung von 2. – wim

Verwandte Themen