2016-07-05 2 views
6

I haben eine ausgerichtete Reihe von ganzen Zahlen in einem Speicher enthalten Indizes I0, I1, I2, I3. Mein Ziel ist es, sich in ein __m256i Register zu erhalten, enthaltend I 0, I 0 + 1, I1, I1 + 1, I2, I2 + 1, I3, I3 + 1. Der schwierige Teil ist sie in die 256-Bit-Register als I0 bekommen, I0 , I1, I1, I2, I2, I3, I3, nach dem ich einfach ein Register hinzufügen kann, das 0, 1, 0, 1, 0, 1, 0, 1 enthält.AVX2 Wie Effizientes Vier Ganze Zahlen laden zu Selbst-Indizes eines 256-Bit-Register und kopieren Odd-Indizes?

Ich fand die intrinsische, _mm256_castsi128_si256, die Lassen Sie mich die 4 ganzen Zahlen in die unteren 128 Bits des 256-Bit-Registers laden, aber ich habe Mühe, die besten von dort zu verwendenden Eigenarten zu finden.

Jede Hilfe wäre willkommen. Ich habe Zugriff auf alle SSE-Versionen, AVX und AVX2 und möchte dies nur mit Intrinsics tun.

Edit:

Ich denke, das funktioniert, aber ich bin nicht, wie effizient es ist ... in dem Prozess der Prüfung.

// _mm128_load_si128: Loads 4 integer values into a temporary 128bit register. 
// _mm256_broadcastsi128_si256: Copies 4 integer values in the 128 bit register to the low and high 128 bits of the 256 bit register. 
__m256i tmpStuff = _mm256_broadcastsi128_si256 ((_mm_load_si128((__m128i*) indicesArray))); 

// _mm256_unpacklo_epi32: Interleaves the integer values of source0 and source1. 
__m256i indices = _mm256_unpacklo_epi32(tmpStuff, tmpStuff); 

__m256i regToAdd = _mm256_set_epi32 (0, 1, 0, 1, 0, 1, 0, 1); 
indices = _mm256_add_epi32(indices, regToAdd); 

Edit2: Der obige Code funktioniert nicht, weil _mm256_unpacklo_epi32 nicht so, wie ich dachte verhält. Der obige Code führt zu I0, I0 + 1, I1, I1 + 1, I0, I0 + 1, I1, I1 + 1.

Edit3: Der folgende Code funktioniert, wieder obwohl ich nicht sicher bin, ob es am effizientesten ist:

__m256i tmpStuff = _mm256_castsi128_si256(_mm_loadu_si128((__m128i*) indicesArray)); 
__m256i mask = _mm256_set_epi32 (3, 3, 2, 2, 1, 1, 0, 0); 
__m256i indices= _mm256_permutevar8x32_epi32(tmpStuff, mask); 
__m256i regToAdd = _mm256_set_epi32 (1, 0, 1, 0, 1, 0, 1, 0); // Set in reverse order. 
indices= _mm256_add_epi32(indices, regToAdd); 

Antwort

6

Ihre zweite Version ist so effizient, wie es möglich ist, es sei denn ich einen Weg bin fehlt das könnte den Shuffle in eine 128-b-Last falten. Das könnte etwas für den Uplink-Durchsatz in fusionierten Domänen helfen, aber nicht für unfusioned-domain.

1 Last (vmovdqa), 1 shuffle (vpermd, aka _mm256_permutevar8x32_epi32) und 1 add (vpaddd) ist ziemlich leicht. Wenn Sie eine nicht ausgerichtete 256-B-Last mit der Aufteilung zwischen den Elementen 1 und 2 nicht ausführen, benötigen Sie eine Art Lane-Crossing-Shuffle, um die oberen 2 Elemente in die oberen 128 B zu bringen.

Da Sie AVX2 verwenden können, Ihre Lösung ist groß, wenn für vpermd einen Shuffle-Maske Laden ist kein Problem. (Registerdruck/Cache-Misses).


Eine Alternative, die eine Shuffle-Maske Vektor konstant vermeidet, ist aber schlechter sonst:

vpmovzxdq eine weitere Option ist es, die oberen beiden Elemente in die obere 128bit Spur für immer.

vpmovzxdq ymm0, [src] 
vpshufd ymm1, ymm0, _MM_SHUFFLE(2,2, 0,0) ; duplicate elements 
vpaddd  ... 

Oder möglicherweise ein höherer Durchsatz, wenn der Shuffle-Port zu einem Engpass für die gesamte Schleife ist. (Noch schlimmer als die vpermd Version in der Frage, though.)

vpmovzxdq ymm0, [src] 
vpsrlq  ymm1, ymm0,32  ; left shift by 32 
vpaddd  ...     ; ymm1 +=1 in odd elements only 
vpor  ...     ; OR the incremented odd elements with the original even elements 

Verschiebung und oder ersetzen Sie die in-Spur shuffle.