2015-10-17 3 views
6

Wenn mit GCC kompiliert 5.2 mit -std=c99, -O3 und -mavx2, die folgende Codebeispiel Auto-vektorisiert (assembly here):Wie vektorisiert fortschreitende Schreibvorgänge mit GCC?

#include <stdint.h> 

void test(uint32_t *restrict a, 
      uint32_t *restrict b) { 
    uint32_t *a_aligned = __builtin_assume_aligned(a, 32); 
    uint32_t *b_aligned = __builtin_assume_aligned(b, 32); 

    for (int i = 0; i < (1L << 10); i += 2) { 
    a_aligned[i] = 42 * b_aligned[i]; 
    a_aligned[i+1] = 3 * a_aligned[i+1]; 
    } 
} 

Aber die folgende Codebeispiel nicht automatisch vectorize (assembly here):

#include <stdint.h> 

void test(uint32_t *restrict a, 
      uint32_t *restrict b) { 
    uint32_t *a_aligned = __builtin_assume_aligned(a, 32); 
    uint32_t *b_aligned = __builtin_assume_aligned(b, 32); 

    for (int i = 0; i < (1L << 10); i += 2) { 
    a_aligned[i] = 42 * b_aligned[i]; 
    a_aligned[i+1] = a_aligned[i+1]; 
    } 
} 

Der einzige Unterschied zwischen den Proben ist der Skalierungsfaktor a_aligned[i+1].

Dies war auch der Fall für GCC 4.8, 4.9 und 5.1. Durch Hinzufügen der volatile zu a_aligned Deklaration wird die automatische Vektorisierung vollständig verhindert. Das erste Beispiel läuft für uns konsistent schneller als das zweite, mit einer stärkeren Beschleunigung für kleinere Typen (z. B. uint8_t anstelle von uint32_t).

Gibt es eine Möglichkeit, das zweite Codebeispiel mit GCC zu vektorisieren?

+0

Also der einzige Unterschied ist der Skalierungsfaktor (3 gegen nichts)? Versuchen Sie, 1 explizit als Skalierungsfaktor hinzuzufügen. Wenn das das Problem löst, ist das ein Compilerfehler. – Jeff

+0

Oder versuchen Sie, die Anweisung 'a_aligned [i + 1] = a_aligned [i + 1]' zu kommentieren oder sie als 'a_aligned [i + 1] * = 1' umzuschreiben. Der Compiler weiß möglicherweise nicht, was er mit Ihrer No-Op-Selbstzuweisung machen soll, außer genau das zu tun, was Sie ihm gesagt haben. –

+0

@Jeff Tatsächlich ist der einzige Unterschied der Skalierungsfaktor. Durch Hinzufügen einer expliziten 1 wird das zweite Codebeispiel nicht automatisch vektorisiert ([assembly here] (https://goo.gl/dnjSaQ)). –

Antwort

1

Folgende Version vectorises, aber das ist hässlich, wenn Sie mich fragen ...

#include <stdint.h> 

void test(uint32_t *a, uint32_t *aa, 
      uint32_t *restrict b) { 
    #pragma omp simd aligned(a,aa,b:32) 
    for (int i = 0; i < (1L << 10); i += 2) { 
    a[i] = 2 * b[i]; 
    a[i+1] = aa[i+1]; 
    } 
} 

Um mit -fopenmp zu kompilieren und mit test(a, a, b) zu nennen.

+0

Es gibt zwei Einschränkungen für diesen Ansatz. Die erste besteht darin, dass Sie das Schlüsselwort "restrict" für "a" und "aa" verlieren, was dazu führen kann, dass Sie "aa" mehr als nötig zurücklesen. Es ist in Ordnung für den bereitgestellten Beispielcode (in diesem Fall verzweigt GCC zu dem nicht vektorisierten Code, wenn "a" und "aa" Alias ​​in einer mühsamen Weise), aber im Allgemeinen könnte es mehr Lesevorgänge als notwendig ergeben. Betrachten wir zum Beispiel, ob "b" durch "a" ersetzt wurde; In diesem Fall werden viele zusätzliche 'vmovdq'-Anweisungen generiert, die nicht benötigt werden. –

+0

Die zweite ist, dass GCC bei '-O3'' test() 'automatisch inline einfügt. Das heißt an Stellen, an denen "test()" Inlines angezeigt wird, dass "a" "aa" ist und nicht automatisch vektorisiert wird. Dies kann mit GCCs "__attribute__ ((noinline))" behoben werden, aber dies verursacht unnötigerweise unnötigen Funktionsaufruf. –