Ausgerichteter und nicht ausgerichteter Speicherzugriff mit AVX / AVX2 intrinsics

Laut Intel Software Developer Manual (Abschnitt 14.9) hat AVX die Ausrichtungsanforderungen für Speicherzugriffe gelockert. Wenn Daten direkt in eine Verarbeitungsanweisung geladen werden, z. B.

vaddps ymm0,ymm0,YMMWORD PTR [rax]

Die Ladeadresse muss nicht ausgerichtet werden. Wenn jedoch ein dedizierter Befehl für ausgerichtetes Laden verwendet wird, z. B.

vmovaps ymm0,YMMWORD PTR [rax]

Die Ladeadresse muss ausgerichtet sein (auf ein Vielfaches von 32), andernfalls wird eine Ausnahme ausgelöst.

Was mich verwirrt, ist die automatische Codegenerierung von intrinsics, in meinem Fall von gcc / g ++ (4.6.3, Linux). Bitte schauen Sie sich den folgenden Testcode an:

#include <x86intrin.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

#define SIZE (1L << 26)
#define OFFSET 1

int main() {
  float *data;
  assert(!posix_memalign((void**)&data, 32, SIZE*sizeof(float)));
  for (unsigned i = 0; i < SIZE; i++) data[i] = drand48();
  float res[8]  __attribute__ ((aligned(32)));
  __m256 sum = _mm256_setzero_ps(), elem;
  for (float *d = data + OFFSET; d < data + SIZE - 8; d += 8) {
    elem = _mm256_load_ps(d);
    // sum = _mm256_add_ps(elem, elem);
    sum = _mm256_add_ps(sum, elem);
  }
  _mm256_store_ps(res, sum);
  for (int i = 0; i < 8; i++) printf("%g ", res[i]); printf("\n");
  return 0;
}

(Ja, ich weiß, dass der Code fehlerhaft ist, da ich eine ausgerichtete Last für nicht ausgerichtete Adressen verwende, aber mit mir tragen ...)

Ich kompiliere den Code mit

g++ -Wall -O3 -march=native -o memtest memtest.C

auf einer CPU mit AVX. Wenn ich den von g ++ generierten Code mit @ überprü

objdump -S -M intel-mnemonic memtest | more

Ich sehe, dass der Compiler keine ausgerichtete Ladeanweisung generiert, sondern die Daten direkt in die Vektoradditionsanweisung lädt:

vaddps ymm0,ymm0,YMMWORD PTR [rax]

Der Code wird problemlos ausgeführt, obwohl die Speicheradressen nicht ausgerichtet sind (OFFSET ist 1). Dies ist klar, da vaddps nicht ausgerichtete Adressen toleriert.

Wenn ich die Zeile mit dem eigentlichen zweiten Zusatz auskommentiere, kann der Compiler die Last und den Zusatz nicht zusammenführen, da vaddps nur einen einzigen Speicherquellenoperanden haben kann und Folgendes generiert:

vmovaps ymm0,YMMWORD PTR [rax]
vaddps ymm1,ymm0,ymm0
vaddps ymm0,ymm1,ymm0

Und jetzt ist das Programm fehlerhaft, da ein dedizierter Befehl zum Ausrichten des Ladevorgangs verwendet wird, die Speicheradresse jedoch nicht ausgerichtet ist. (Das Programm ist übrigens nicht fehlerhaft, wenn ich _mm256_loadu_ps verwende oder wenn ich OFFSET auf 0 setze.)

Dies überlässt den Programmierer der Gnade des Compilers und macht das Verhalten meiner bescheidenen Meinung nach teilweise unvorhersehbar.

Meine Frage lautet: Gibt es eine Möglichkeit, den C-Compiler zu zwingen, entweder eine direkte Last in einer Verarbeitungsanweisung (wie vaddps) zu generieren oder eine dedizierte Ladeanweisung (wie vmovaps) zu generieren?

Antworten auf die Frage(4)

Ihre Antwort auf die Frage