2015-06-19 17 views
8

ich ein Stück Code bin Vektorisierung und irgendwann habe ich folgendes Setup:Multiply-subtrahieren

register m128 a = { 99,99,99,99,99,99,99,99 } 
register m128 b = { 100,50,119,30,99,40,50,20 } 

Ich bin zur Verpackung short s in diesen Registern, weshalb ich 8 Werte pro registrieren. Ich möchte das i-te Element in b mit dem entsprechenden Wert in a subtrahieren, wenn der i-te Wert von b größer oder gleich dem Wert in a ist (In diesem Fall wird a mit der Konstante gefüllt 99). Zu diesem Zweck verwende ich zunächst eine größer oder gleich zwischen b und a, die Ausbeuten für dieses Beispiel:

register m128 c = { 1,0,1,0,1,0,0,0 } 

Um den Vorgang abzuschließen, Ich mag würde die Multiplikations- und subtrahieren verwenden , dh in b die Operation b -= a*c zu speichern. Das Ergebnis wäre dann:

b = { 1,50,20,30,0,40,50,20 } 

Gibt es irgendeine Operation, die so etwas tut? Was ich gefunden habe, waren fusionierte Operationen für Haswell, aber ich arbeite gerade an Sandy-Bridge. Auch, wenn jemand eine bessere Idee, dies zu tun hat, lassen Sie es mich wissen (zB ich könnte eine logische subtrahieren tun. Wenn 1 in c dann ziehe ich, nichts sonst

+0

Nur um zu klären, sollte Ihr Betrieb 'b - = a * c ', die entsprechende Subtraktion durchzuführen? – Alejandro

+0

Richtig, @Alejandro – a3mlord

Antwort

0

Sie können b-c kopieren, subtrahieren a aus c, eine arithmetische Verschiebung ausführen rechts um 15 Positionen in den 16-Bit-Werten, den Wert von c ergänzen, Maske c mit a und schließlich c von b subtrahiert.

bin ich für die Spezifika-Syntax nicht vertraut, aber die Schritte sind:

register m128 c = b; 
c -= a; 
c >>= 15; 
c = ~c; 
c &= a; 
b -= c; 

hier ist eine Alternative mit weniger Schritten:

register m128 c = compare_ge(b, a); 
c = -c; 
c &= a; 
b -= c; 
3

Sie im Wesentlichen eine SSE-Version dieses Code will, nicht wahr?

if (b >= a) 
    t = b-a 
else 
    t = b 
b = t 

Da wir wollen conditionals für die SSE-Version vermeiden, damit wir von der Steuerablauf wie diese loszuwerden (beachten Sie, dass die Maske invertiert wird):

uint16_t mask = (b>=a)-1 
uint16_t tmp = b-a; 
uint16_t d = (b & mask) | (tmp & ~mask) 
b = d 

Ich habe überprüft die _mm_cmpgt_epi16 intrinsisch und es hat eine schöne Eigenschaft, dass es entweder 0x0000 für false oder 0xFFFF für wahr, anstelle von einem einzigen Bit 0 oder 1 (wodurch die Notwendigkeit für die erste Subtraktion entfällt) zurückgibt. Daher könnte unsere SSE-Version so aussehen.

__m128i mask = _mm_cmpgt_epi16 (b, a) 
__m128i tmp = _mm_sub_epi16 (b, a) 
__m128 d = _mm_or_ps (_mm_and_ps (mask, tmp), _mm_andnot_ps (mask, b)) 

EDIT: harold hat eine weit weniger komplizierte Antwort erwähnt. Die obige Lösung kann hilfreich sein, wenn Sie den else Teil der if/else ändern müssen.

uint16_t mask = ~((b>=a)-1) 
uint16_t tmp = a & mask 
b = b - tmp 

wird die SSE-Code

sein
__m128i mask = _mm_cmpgt_epi16 (b, a) 
__m128i t = _mm_sub_epi16 (b, _mm_and_si128 (mask, a)) 
+0

Außer, dass Sie 'd' verwendet haben, wo ich' c' habe, ja. Ihre Operation 'b = d' wird in meinem Fall nicht benötigt, da ich mit Out-Place-Operationen einverstanden bin. Ich denke du willst nicht m = (b> = a) -1' sondern 'm = (b> = a)', oder? – a3mlord

+0

Nein. Dein 'c' ist mein' m' (für Maske). Die 'd'-Variable dient nur dazu, sie lesbarer zu machen. Auch ist das 'm = (b> = a) -1' korrekt, weil wir dann eine einzelne Bitmaske (0 oder 1) in eine 8-Bit-Maske (0000000 oder 11111111) konvertieren, die wir für die nächste Zeile benötigen. – hayesti

+0

@ a3mlord Ich habe mir den "mm_cmpgt' intrinsic" genauer angeschaut. Es gibt tatsächlich entweder 0x00 oder 0xFF zurück, so dass wir beim zweiten Gedanken die -1 nicht brauchen werden, aber wir müssen die Reihenfolge der Operatoren and ändern. Ich werde meinen Beitrag jetzt bearbeiten ... – hayesti