2015-06-17 2 views
16

Ich schrieb etwas Code mit statischen Arrays und es vektorisiert einfach gut.Sag C++, dass Zeigerdaten 16 Byte ausgerichtet sind

float data[1024] __attribute__((aligned(16))); 

Ich möchte die Arrays dynamisch zugewiesen werden. Ich habe versucht, so etwas wie dies zu tun:

float *data = (float*) aligned_alloc(16, size*sizeof(float)); 

Aber der Compiler (GCC 4.9.2), nicht mehr den Code vektorisiert. Ich nehme an, dies ist, weil es nicht weiß, dass die Zeigerdaten 16 Byte ausgerichtet sind. Ich erhalte Meldungen wie:

note: Unknown alignment for access: *_43 

Ich habe versucht, das Hinzufügen dieser Zeile, bevor die Daten verwendet wird, aber es scheint nicht, etwas zu tun:

data = (float*) __builtin_assume_aligned(data, 16); 

eine andere Variable verwenden und restrict tat nicht Hilfe:

float* __restrict__ align_data = (float*) __builtin_assume_aligned(data,16); 

Beispiel:

#include <iostream> 
#include <stdlib.h> 
#include <math.h> 

#define SIZE 1024 
#define DYNAMIC 0 
#define A16 __attribute__((aligned(16))) 
#define DA16 (float*) aligned_alloc(16, size*sizeof(float)) 

class Test{ 
public: 
    int size; 
#if DYNAMIC 
    float *pos; 
    float *vel; 
    float *alpha; 
    float *k_inv; 
    float *osc_sin; 
    float *osc_cos; 
    float *dosc1; 
    float *dosc2; 
#else 
    float pos[SIZE] A16; 
    float vel[SIZE] A16; 
    float alpha[SIZE] A16; 
    float k_inv[SIZE] A16; 
    float osc_sin[SIZE] A16; 
    float osc_cos[SIZE] A16; 
    float dosc1[SIZE] A16; 
    float dosc2[SIZE] A16; 
#endif 
    Test(int arr_size){ 
     size = arr_size; 
#if DYNAMIC 
     pos = DA16; 
     vel = DA16; 
     alpha = DA16; 
     k_inv = DA16; 
     osc_sin = DA16; 
     osc_cos = DA16; 
     dosc1 = DA16; 
     dosc2 = DA16; 
#endif 
    } 
    void compute(){ 
     for (int i=0; i<size; i++){ 
      float lambda = .67891*k_inv[i], 
       omega = (.89 - 2*alpha[i]*lambda)*k_inv[i], 
       diff2 = pos[i] - omega, 
       diff1 = vel[i] - lambda + alpha[i]*diff2; 
      pos[i] = osc_sin[i]*diff1 + osc_cos[i]*diff2 + lambda*.008 + omega; 
      vel[i] = dosc1[i]*diff1 - dosc2[i]*diff2 + lambda; 
     } 
    } 
}; 

int main(int argc, char** argv){ 
    Test t(SIZE); 
    t.compute(); 
    std::cout << t.pos[10] << std::endl; 
    std::cout << t.vel[10] << std::endl; 
} 

Hier ist, wie ich bin Kompilieren:

g++ -o test test.cpp -O3 -march=native -ffast-math -fopt-info-optimized 

Wenn DYNAMIC-0 gesetzt ist, gibt sie:

test.cpp:46:4: note: loop vectorized 

aber wenn es um 1 gesetzt ist es gibt nichts.

+0

Haben Sie versucht, mit neuen? – cup

+0

habe ich noch nicht; Ich dachte nicht, dass es eine 16-Byte-Ausrichtung garantieren könnte. – Azmisov

+0

W.R.T 'neu', siehe [diese Antwort] (http: // stackoverflow.com/a/16510895/703016). –

Antwort

6

Der Compiler vektorisiert die Schleife nicht, da er nicht feststellen kann, dass die dynamisch zugewiesenen Zeiger sich nicht gegenseitig aliasieren. Eine einfache Möglichkeit, Ihren Beispielcode vektorisieren zu lassen, besteht darin, die Option --param vect-max-version-for-alias-checks=1000 zu übergeben. Dadurch kann der Compiler alle erforderlichen Prüfungen ausgeben, um festzustellen, ob die Zeiger tatsächlich Aliasing sind.

Eine andere einfache Lösung, damit Sie Ihren Beispielcode vektorisieren können, ist die Umbenennung main, wie von Marc Glisse in seinem Kommentar vorgeschlagen. Funktionen mit dem Namen main haben anscheinend bestimmte Optimierungen deaktiviert. Etwas anderes genannt, GCC 4.9.2 kann die Verwendung von this->foo (und die anderen Zeiger-Mitglieder) in compute zurück zu ihren Zuordnungen in Test() verfolgen.

Ich nehme jedoch an, dass etwas anderes als Ihre Klasse in einer Funktion mit dem Namen main verhindert wurde, dass Ihr Code in Ihrem echten Code vektorisiert wird. Eine allgemeinere Lösung, die es Ihrem Code ermöglicht, ohne Alias- oder Alignment-Prüfungen zu vektorisieren, besteht darin, das Schlüsselwort restrict und das Attribut aligned zu verwenden. Etwas wie folgt aus:

typedef float __attribute__((aligned(16))) float_a16; 

__attribute__((noinline)) 
static void _compute(float_a16 * __restrict__ pos, 
     float_a16 * __restrict__ vel, 
     float_a16 * __restrict__ alpha, 
     float_a16 * __restrict__ k_inv, 
     float_a16 * __restrict__ osc_sin, 
     float_a16 * __restrict__ osc_cos, 
     float_a16 * __restrict__ dosc1, 
     float_a16 * __restrict__ dosc2, 
     int size) { 
    for (int i=0; i<size; i++){ 
     float lambda = .67891*k_inv[i], 
      omega = (.89 - 2*alpha[i]*lambda)*k_inv[i], 
      diff2 = pos[i] - omega, 
      diff1 = vel[i] - lambda + alpha[i]*diff2; 
     pos[i] = osc_sin[i]*diff1 + osc_cos[i]*diff2 + lambda*.008 + omega; 
     vel[i] = dosc1[i]*diff1 - dosc2[i]*diff2 + lambda; 
    } 
} 

void compute() { 
    _compute(pos, vel, alpha, k_inv, osc_sin, osc_cos, dosc1, dosc2, 
     size); 
} 

Das noinline Attribut kritisch ist, sonst inlining können die Zeiger verursachen ihre Beschränkt und Ausrichtung zu verlieren. Der Compiler scheint das Schlüsselwort restrict in anderen Kontexten als Funktionsparametern zu ignorieren.

+0

Ah, sehr nett. Gibt es eine Möglichkeit, das Schlüsselwort "restrict" zu verwenden, ohne alles in eine separate Funktion zu übertragen? Es scheint mühsam, dies für jede Funktion zu tun. – Azmisov

+0

@Azmisov Leider ist dies der einzige Weg, den ich gefunden habe, der funktioniert hat. –

+0

Ich glaube, die Handhabung von restric mit inlining wurde in gcc-5 verbessert und die Funktionen aligned_alloc und posix_memalign wurden wie malloc als disjunkte Speicherbereiche markiert, aber der Compiler stürzt einfach auf diesem Test ab :-( –

Verwandte Themen