2012-06-08 6 views
14

Dies ist mein allererstes Arbeiten mit SSE-Intrinsics. Ich versuche, ein einfaches Stück Code in eine schnellere Version mit Intel SSE intrinsisch (bis SSE4.2) zu konvertieren. Ich scheine auf eine Reihe von Fehlern zu stoßen.Optimieren von Code mit Intel SSE-Intrinsics für die Vektorisierung

Die skalare Version des Codes ist: (einfache Multiplikation Matrix)

 void mm(int n, double *A, double *B, double *C) 
    { 
     int i,j,k; 
     double tmp; 

     for(i = 0; i < n; i++) 
      for(j = 0; j < n; j++) { 
        tmp = 0.0; 
        for(k = 0; k < n; k++) 
          tmp += A[n*i+k] * 
            B[n*k+j]; 
        C[n*i+j] = tmp; 

       } 
      } 

Dies ist meine Version: Ich #include

 void mm_sse(int n, double *A, double *B, double *C) 
     { 
     int i,j,k; 
     double tmp; 
     __m128d a_i, b_i, c_i; 

     for(i = 0; i < n; i++) 
      for(j = 0; j < n; j++) { 
        tmp = 0.0; 
        for(k = 0; k < n; k+=4) 
          a_i = __mm_load_ps(&A[n*i+k]); 
          b_i = __mm_load_ps(&B[n*k+j]); 
          c_i = __mm_load_ps(&C[n*i+j]); 

          __m128d tmp1 = __mm_mul_ps(a_i,b_i); 
          __m128d tmp2 = __mm_hadd_ps(tmp1,tmp1); 
          __m128d tmp3 = __mm_add_ps(tmp2,tmp3); 
          __mm_store_ps(&C[n*i+j], tmp3); 

      } 
     } 

Wo enthalten sind, bin ich mit diesem schief gehen? Ich erhalte mehrere Fehler wie folgt aus:

mm_vec.c (84): Fehler: ein Wert vom Typ "int" kann nicht auf eine Entität vom Typ zugeordnet werden "__m128d" a_i = __mm_load_ps (& A [n * i + k]); Diese

ist, wie ich bin Kompilieren: icc -O2 mm_vec.c -o vec

Kann jemand bitte helfen Sie mir diesen Code genau zu konvertieren. Vielen Dank!

UPDATE:

Ihre Vorschläge nach, ich habe folgende Änderungen vorgenommen:

 void mm_sse(int n, float *A, float *B, float *C) 
     { 
     int i,j,k; 
     float tmp; 
     __m128 a_i, b_i, c_i; 

     for(i = 0; i < n; i++) 
      for(j = 0; j < n; j++) { 
        tmp = 0.0; 
        for(k = 0; k < n; k+=4) 
          a_i = _mm_load_ps(&A[n*i+k]); 
          b_i = _mm_load_ps(&B[n*k+j]); 
          c_i = _mm_load_ps(&C[n*i+j]); 

          __m128 tmp1 = _mm_mul_ps(a_i,b_i); 
          __m128 tmp2 = _mm_hadd_ps(tmp1,tmp1); 
          __m128 tmp3 = _mm_add_ps(tmp2,tmp3); 
          _mm_store_ps(&C[n*i+j], tmp3); 


      } 
     } 

Aber jetzt scheine ich einen Segmentation fault zu bekommen. Ich weiß das vielleicht, weil ich nicht richtig auf Array-Indizes für Array A, B, C zugreife. Ich bin sehr neu und weiß nicht, wie ich damit verfahren soll.

Bitte helfen Sie mir, den richtigen Umgang mit diesem Code zu bestimmen.

Antwort

9

Der Fehler Sie sehen, ist, weil Sie zu viele Unterstreichungen in den Funktionsnamen haben, zum Beispiel:

__mm_mul_ps 

sein sollte:

_mm_mul_ps // Just one underscore up front 

so der C-Compiler wird vorausgesetzt, sie int zurückkehren seit es keine Deklaration gesehen hat.

Darüber hinaus gibt es jedoch weitere Probleme - Sie scheinen Anrufe auf Double- und Single-Float-Varianten derselben Anweisung zu mischen.

Zum Beispiel haben Sie:

__m128d a_i, b_i, c_i; 

aber Sie rufen:

__mm_load_ps(&A[n*i+k]); 

, die ein __m128 gibt kein __m128d - Sie nennen wollte:

_mm_load_pd 

statt. Ebenso für die anderen Anweisungen, wenn Sie möchten, dass sie an Doppelpaaren arbeiten.


Wenn Sie unerklärliche Zugriffsfehler sind zu sehen und in SSE-Code würde ich geneigt sein, zu erraten, dass Sie Speicherausrichtung Probleme haben - Zeiger auf SSE intrinsics geben (meist) 16 Jahre alt sein müssen Byte ausgerichtet. Sie können check this with a simple assert in Ihrem Code, oder es in einem Debugger überprüfen (die letzte Ziffer des Zeigers erwarten 0 sein, wenn sie richtig ausgerichtet ist).

Wenn es nicht richtig ausgerichtet ist, müssen Sie sicherstellen, dass es ist. Für Dinge mit nicht zugeordnet new/malloc() können Sie dies mit einem Compiler Erweiterung (zB with gcc):

float a[16] __attribute__ ((aligned (16))); 

Sofern Ihre Version von gcc eine maximale Ausrichtung groß genug ist, diese und einige andere Einsprüche über Stapel Ausrichtung zu unterstützen . Für dynamisch zugewiesenen Speicher sollten Sie eine plattformspezifische Erweiterung verwenden, z. posix_memalign geeignete Speicher zuzuordnen:

float *a=NULL; 
posix_memalign(&a, __alignof__(__m128), sizeof(float)*16); 

(Ich denke, es könnte schöner, portable Möglichkeiten, dies zu tun, mit 11 C++, aber ich bin nicht 100% sicher auf, dass noch) nicht.

Es gibt einige Anweisungen, die Ihnen erlauben, unausgerichtete Lasten und Speicher zu tun, aber sie sind im Vergleich zu ausgerichteten Lasten schrecklich langsam und sollten, wenn überhaupt möglich, vermieden werden.

+0

ich mit icc arbeite nicht gcc. Glauben Sie, dass es wie folgt Handhabung: a_i = _mm_load_ps (& A [n * i + k]), der richtige Ansatz ist? Die Beispiele, die ich an anderer Stelle (auch in der Intel Intrinsic-Dokumentation) gesehen habe, haben sehr einfache Beispiele. Die Arrays A, B, C wurden alle mit malloc zugeordnet. – PGOnTheGo

+1

@Hello_PG Die Ladung selbst ist nicht direkt falsch. Sie müssen c_i jedoch nicht laden. Zum größten Teil ICC habe die gleichen Erweiterungen wie gcc - ich denke, das ist der Fall für die Ausrichtung ein, ich bin besser vertraut mit GCC als ICC persönlich obwohl so qualifizierte ich es mit, dass und auf die Dokumentation verknüpfen ich wusste, wie zu finden. malloc garantiert nicht die korrekte Ausrichtung auf allen Plattformen, daher wird wahrscheinlich posix_memign benötigt. Hat die von mir vorgeschlagene Behauptung versagt? – Flexo

+0

Wenn ich versuche, wie dies für einen Speicher zuzuweisen: A = (float *) _ aligned_malloc (dimension * Maß * sizeof (float), 16); Ich bekomme einen Kompilierfehler: undefinierter Verweis auf 'aligned_malloc' mit icc. So kompiliere ich: icc -O2 mm_vec.c -o vec2 – PGOnTheGo

3

Sie müssen sicherstellen, dass Ihre Lade- und Speichervorgänge sind immer 16 Byte ausgerichtet Adressen zugreifen. Alternativ kann diese, wenn Sie nicht garantieren können, aus irgendeinem Grund, verwenden Sie dann _mm_loadu_ps/_mm_storeu_ps statt _mm_load_ps/_mm_store_ps - dies weniger effizient sein wird, aber nicht auf fehlerhaft ausgerichtete Adressen zum Absturz bringen.