2016-11-11 6 views
0

Hallo Ich versuche, die Leistung dieses Codes zu verbessern, unter der Voraussetzung, dass ich eine Maschine habe, die 4 Threads verarbeiten kann. Ich dachte zuerst darüber nach, omp parallel zu machen, aber dann sah ich, dass diese Funktion innerhalb einer for-Schleife war, also war es nicht sehr effizient, Threads so oft zu erstellen. So würde Ich mag wissen, wie es mit SSE zu implementieren, die effizienter wäre:SSE Parallelisierung

unsigned char cubicInterpolate_paralelo(unsigned char p[4], unsigned char x) { 
    unsigned char resultado; 
    unsigned char intermedio; 
    intermedio = + x*(3.0*(p[1] - p[2]) + p[3] - p[0]); 

    resultado = p[1] + 0.5 * x *(p[2] - p[0] + x*(2.0*p[0] - 5.0*p[1] + 4.0*p[2] - p[3] + x*(3.0*(p[1] - p[2]) + p[3] - p[0]))); 
    return resultado; 
} 

unsigned char bicubicInterpolate_paralelo (unsigned char p[4][4], unsigned char x, unsigned char y) { 
    unsigned char arr[4],valorPixelCanal; 
    arr[0] = cubicInterpolate_paralelo(p[0], y); 
    arr[1] = cubicInterpolate_paralelo(p[1], y); 
    arr[2] = cubicInterpolate_paralelo(p[2], y); 
    arr[3] = cubicInterpolate_paralelo(p[3], y); 

    valorPixelCanal = cubicInterpolate_paralelo(arr, x); 
    return valorPixelCanal; 
} 

dies in einige für geschachtelt verwendet wird:

for(i=0; i<z_img.width(); i++) { 
     for(j=0; j<z_img.height(); j++) { 
      //For R,G,B 
      for(c=0; c<3; c++) { 

       for(l=0; l<4; l++){ 
        for(k=0; k<4; k++){ 

         arr[l][k] = img(i/zFactor +l, j/zFactor +k, 0, c); 
        } 
       } 

       color[c] = bicubicInterpolate_paralelo(arr, (unsigned char)(i%zFactor)/zFactor, (unsigned char)(j%zFactor)/zFactor); 
      } 
      z_img.draw_point(i,j,color); 
     } 
    } 
+1

Nur pingelig, aber mit SSE oder einem anderen Satz von Vektoroperationen wird Vektorisierung und keine Parallelisierung genannt. –

Antwort

3

Ich habe einige Freiheiten mit dem Code genommen, so dass Sie kann es erheblich ändern müssen, aber hier ist eine (nicht getestet) Umschrift zu SSE:

__m128i x = _mm_unpacklo_epi8(_mm_loadl_epi64(x_array), _mm_setzero_si128()); 
__m128i p0 = _mm_unpacklo_epi8(_mm_loadl_epi64(p0_array), _mm_setzero_si128()); 
__m128i p1 = _mm_unpacklo_epi8(_mm_loadl_epi64(p1_array), _mm_setzero_si128()); 
__m128i p2 = _mm_unpacklo_epi8(_mm_loadl_epi64(p2_array), _mm_setzero_si128()); 
__m128i p3 = _mm_unpacklo_epi8(_mm_loadl_epi64(p3_array), _mm_setzero_si128()); 
__m128i t = _mm_sub_epi16(p1, p2); 
t = _mm_add_epi16(_mm_add_epi16(t, t), t); // 3 * (p[1] - p[2]) 
__m128i intermedio = _mm_mullo_epi16(x, _mm_sub_epi16(_mm_add_epi16(t, p3), p0)); 
t = _mm_add_epi16(p1, _mm_slli_epi16(p1, 2)); // 5 * p[1] 
// t2 = 2 * p[0] + 4 * p[2] 
__m128i t2 = _mm_add_epi16(_mm_add_epi16(p0, p0), _mm_slli_epi16(p2, 2)); 
t = _mm_mullo_epi16(x, _mm_sub_epi16(_mm_add_epi16(t2, intermedio), _mm_add_epi16(t, p3))); 
t = _mm_mullo_epi16(x, _mm_add_epi16(_mm_sub_epi16(p2, p0), t)); 
__m128i resultado = _mm_add_epi16(p1, _mm_srli_epi16(t, 1)); 
return resultado; 

Die 16-Bit-Zwischenprodukte, die ich sollte breit sein verwenden genug, der einzige Weg für Informationen von den hohen Bits, um niedrige Bits in diesem Code zu beeinflussen, ist die richtige Verschiebung um 1 (0.5 * in Ihrem Code), also wirklich brauchen wir nur 9 Bits, der Rest kann das Ergebnis nicht beeinflussen. Bytes wären nicht breit genug (es sei denn, Sie haben einige zusätzliche Garantien, von denen ich nichts weiß), aber sie würden sowieso ärgerlich sein, weil es keine gute Möglichkeit gibt, sie zu multiplizieren.

ich die Einfachheit halber gab vor, dass die Eingabe in Form von zusammenhängenden Arrays von x ‚s, p[0]‘ nimmt s etc, das ist nicht das, was Sie hier brauchen, aber ich habe keine Zeit, das Laden alle und schlurfenden zu erarbeiten.

0

OpenMP verwenden, könnten versuchen Sie das Hinzufügen der #pragma zu die äußerste for-Schleife. Dies sollte dein Problem lösen.

Die SSE-Route ist aufgrund der zusätzlichen Ausrichtungseinschränkungen für Daten schwieriger, aber die einfachste Transformation wäre, cubicInterpolate_paralelo zu erweitern, um mehrere Berechnungen gleichzeitig zu verarbeiten. Wenn Sie dem Compiler sagen, dass er SSE verwenden soll, reicht das zwar für Sie, aber um sicherzustellen, dass Sie auch intrinsische Funktionen und Typen verwenden können.

1

SSE ist mit Threads nicht verwandt. Ein einzelner Thread führt jeweils einen Befehl aus. mit SSE kann dieser einzelne Befehl gleichzeitig auf 4 oder 8 Sätze von Argumenten angewendet werden. Mit mehreren Threads können Sie also mehrere SSE-Anweisungen ausführen, um noch mehr Daten zu verarbeiten.

Sie können Threads mit for-Schleifen verwenden. Benutze sie nicht drinnen. Nimm stattdessen den for(i=0; i<z_img.width(); i++) { äußeren Loop und teile ihn in 4 Bänder von width/4. Thread 0 bekommt 0..width/4, Thread 1 bekommt width/4..width/2 etc.

Zu einer nicht verwandten Anmerkung leidet Ihr Code auch unter dem Mischen von Gleitkomma- und Ganzzahlmathematik. 0.5 * x ist nicht annähernd so effizient wie x/2.