2014-09-02 18 views
9

4x4 행렬을 만들고 각각을 변환하여 8x8 행렬을 전이시킬 수 있습니다. 이것은 내가 원하지 않는 일입니다.AVX/AVX2를 사용하여 8x8 플로트 변환

또 다른 질문으로는 하나의 대답은 gave a solution이며 8x8 매트릭스의 경우 24 개의 명령어 만 필요합니다. 그러나 이것은 부유물에는 적용되지 않습니다.

AVX2에는 256 비트의 레지스터가 포함되어 있으므로 각 레지스터는 8 개의 32 비트 정수 (부동 소수점)에 적합합니다. 그러나 질문은 :

가능한 가장 작은 명령으로 AVX/AVX2를 사용하여 8x8 플로트 매트릭스를 조 변경하는 방법은 무엇입니까?

+0

Hei David. 지그재그 녀석은 녀석이 죽을 때까지 Hva mer vil du ha? –

+0

@ Zboson : 나는 과거의 일들이 정말 바빴지만, 당신의 대답을 받아 들였습니다. 나는 그것을 매우 감사한다! – DavidS

답변

12

.

AVX로 8x8 플로트 매트릭스를 전치시키기위한 솔루션을 반복하겠습니다. 이것이 4x4 블록 및 _MM_TRANSPOSE4_PS을 사용하는 것보다 빠르면 알려주십시오. 나는 더 큰 행렬 변환에서 커널에 사용했는데, 메모리가 바운드되어서 아마도 공정한 테스트가 아니었을 것이다.

inline void transpose8_ps(__m256 &row0, __m256 &row1, __m256 &row2, __m256 &row3, __m256 &row4, __m256 &row5, __m256 &row6, __m256 &row7) { 
__m256 __t0, __t1, __t2, __t3, __t4, __t5, __t6, __t7; 
__m256 __tt0, __tt1, __tt2, __tt3, __tt4, __tt5, __tt6, __tt7; 
__t0 = _mm256_unpacklo_ps(row0, row1); 
__t1 = _mm256_unpackhi_ps(row0, row1); 
__t2 = _mm256_unpacklo_ps(row2, row3); 
__t3 = _mm256_unpackhi_ps(row2, row3); 
__t4 = _mm256_unpacklo_ps(row4, row5); 
__t5 = _mm256_unpackhi_ps(row4, row5); 
__t6 = _mm256_unpacklo_ps(row6, row7); 
__t7 = _mm256_unpackhi_ps(row6, row7); 
__tt0 = _mm256_shuffle_ps(__t0,__t2,_MM_SHUFFLE(1,0,1,0)); 
__tt1 = _mm256_shuffle_ps(__t0,__t2,_MM_SHUFFLE(3,2,3,2)); 
__tt2 = _mm256_shuffle_ps(__t1,__t3,_MM_SHUFFLE(1,0,1,0)); 
__tt3 = _mm256_shuffle_ps(__t1,__t3,_MM_SHUFFLE(3,2,3,2)); 
__tt4 = _mm256_shuffle_ps(__t4,__t6,_MM_SHUFFLE(1,0,1,0)); 
__tt5 = _mm256_shuffle_ps(__t4,__t6,_MM_SHUFFLE(3,2,3,2)); 
__tt6 = _mm256_shuffle_ps(__t5,__t7,_MM_SHUFFLE(1,0,1,0)); 
__tt7 = _mm256_shuffle_ps(__t5,__t7,_MM_SHUFFLE(3,2,3,2)); 
row0 = _mm256_permute2f128_ps(__tt0, __tt4, 0x20); 
row1 = _mm256_permute2f128_ps(__tt1, __tt5, 0x20); 
row2 = _mm256_permute2f128_ps(__tt2, __tt6, 0x20); 
row3 = _mm256_permute2f128_ps(__tt3, __tt7, 0x20); 
row4 = _mm256_permute2f128_ps(__tt0, __tt4, 0x31); 
row5 = _mm256_permute2f128_ps(__tt1, __tt5, 0x31); 
row6 = _mm256_permute2f128_ps(__tt2, __tt6, 0x31); 
row7 = _mm256_permute2f128_ps(__tt3, __tt7, 0x31); 
} 

this comment을 바탕으로 나는 8 × 8 전치을 할 수있는보다 효율적인 방법이 있다는 것을 알게되었다. "11.11 포트 5 압력 처리"단원의 예제 11-19 및 11-20을 Intel optimization manual에서 확인하십시오. 예제 11-19에서는 같은 수의 명령어를 사용하지만 port0으로가는 블렌드를 사용하여 port5의 부담을 줄입니다. 어떤 시점에서 내장 함수를 사용하여 구현할 수 있지만이 시점에서는 필요하지 않습니다.


위에서 언급 한 Intel 설명서에서 예제 11-19와 11-20을 자세히 살펴 보았습니다. 예제 11-19에서는 필요한 것보다 4 개 더 많은 셔플 연산을 사용합니다. 그것은 8 개의 언팩, 12 개의 셔플 및 8 개의 128 비트 순열을 가지고 있습니다. 내 방식에서는 4 개의 셔플을 사용합니다. 그들은 셔플 중 8 개를 블렌드로 대체합니다. 그래서 셔플 4 개와 블렌드 8 개. 나는 단지 8 개의 셔플로 내 방법보다 낫다고 생각한다.

그러나 예제 11-20은 메모리에서 행렬을로드해야하는 경우 개선되었습니다. 8 개의 언팩, 8 개의 인서트, 8 개의 셔플, 8 개의 128 비트로드 및 8 개의 스토어를 사용합니다. 128 비트 부하는 포트 압력을 감소시킵니다. 나는 intrinsics를 사용하여 이것을 구현했다.

//Example 11-20. 8x8 Matrix Transpose Using VINSRTPS 
void tran(float* mat, float* matT) { 
    __m256 r0, r1, r2, r3, r4, r5, r6, r7; 
    __m256 t0, t1, t2, t3, t4, t5, t6, t7; 

    r0 = _mm256_insertf128_ps(_mm256_castps128_ps256(_mm_load_ps(&mat[0*8+0])), _mm_load_ps(&mat[4*8+0]), 1); 
    r1 = _mm256_insertf128_ps(_mm256_castps128_ps256(_mm_load_ps(&mat[1*8+0])), _mm_load_ps(&mat[5*8+0]), 1); 
    r2 = _mm256_insertf128_ps(_mm256_castps128_ps256(_mm_load_ps(&mat[2*8+0])), _mm_load_ps(&mat[6*8+0]), 1); 
    r3 = _mm256_insertf128_ps(_mm256_castps128_ps256(_mm_load_ps(&mat[3*8+0])), _mm_load_ps(&mat[7*8+0]), 1); 
    r4 = _mm256_insertf128_ps(_mm256_castps128_ps256(_mm_load_ps(&mat[0*8+4])), _mm_load_ps(&mat[4*8+4]), 1); 
    r5 = _mm256_insertf128_ps(_mm256_castps128_ps256(_mm_load_ps(&mat[1*8+4])), _mm_load_ps(&mat[5*8+4]), 1); 
    r6 = _mm256_insertf128_ps(_mm256_castps128_ps256(_mm_load_ps(&mat[2*8+4])), _mm_load_ps(&mat[6*8+4]), 1); 
    r7 = _mm256_insertf128_ps(_mm256_castps128_ps256(_mm_load_ps(&mat[3*8+4])), _mm_load_ps(&mat[7*8+4]), 1); 

    t0 = _mm256_unpacklo_ps(r0,r1); 
    t1 = _mm256_unpackhi_ps(r0,r1); 
    t2 = _mm256_unpacklo_ps(r2,r3); 
    t3 = _mm256_unpackhi_ps(r2,r3); 
    t4 = _mm256_unpacklo_ps(r4,r5); 
    t5 = _mm256_unpackhi_ps(r4,r5); 
    t6 = _mm256_unpacklo_ps(r6,r7); 
    t7 = _mm256_unpackhi_ps(r6,r7); 

    r0 = _mm256_shuffle_ps(t0,t2, 0x44); 
    r1 = _mm256_shuffle_ps(t0,t2, 0xEE); 
    r2 = _mm256_shuffle_ps(t1,t3, 0x44); 
    r3 = _mm256_shuffle_ps(t1,t3, 0xEE); 
    r4 = _mm256_shuffle_ps(t4,t6, 0x44); 
    r5 = _mm256_shuffle_ps(t4,t6, 0xEE); 
    r6 = _mm256_shuffle_ps(t5,t7, 0x44); 
    r7 = _mm256_shuffle_ps(t5,t7, 0xEE); 

    _mm256_store_ps(&matT[0*8], r0); 
    _mm256_store_ps(&matT[1*8], r1); 
    _mm256_store_ps(&matT[2*8], r2); 
    _mm256_store_ps(&matT[3*8], r3); 
    _mm256_store_ps(&matT[4*8], r4); 
    _mm256_store_ps(&matT[5*8], r5); 
    _mm256_store_ps(&matT[6*8], r6); 
    _mm256_store_ps(&matT[7*8], r7); 
} 

그래서 다시 예를 11-19로 보았다. 내가 말할 수있는 한 기본 아이디어는 셔플 명령어 2 개를 셔플 1 개와 블렌드 2 개로 대체 할 수 있다는 것입니다.내 원래의 코드는 8 셔플을 사용 예 11-19 4 개 섞어 팔 혼합물을 사용하는 이유는 설명

v = _mm256_shuffle_ps(t0,t2, 0x4E); 
r0 = _mm256_blend_ps(t0, v, 0xCC); 
r1 = _mm256_blend_ps(t2, v, 0x33); 

으로 예를 들어

r0 = _mm256_shuffle_ps(t0,t2, 0x44); 
r1 = _mm256_shuffle_ps(t0,t2, 0xEE); 

교체 할 수있다.

블렌드는 여러 포트로 갈 때 처리량이 더 좋지만 셔플은 한 포트에만 적용됩니다. 그러나 더 나은 것은 8 개의 셔플 또는 4 개의 셔플과 8 개의 블렌드입니까? 이것은 테스트되어야합니다.

그러나 압축 해제 지침을 블렌드로 바꾸는 방법은 없습니다.

여기 예제 2 - 셔플을 1 셔플과 2 블렌드로 변환하는 예 11-19와 VINSRTPS을 사용하는 예 11-20을 결합한 전체 코드입니다.

#include <stdio.h> 
#include <x86intrin.h> 
#include <omp.h> 

/* 
void tran(float* mat, float* matT) { 
    __m256 r0, r1, r2, r3, r4, r5, r6, r7; 
    __m256 t0, t1, t2, t3, t4, t5, t6, t7; 

    r0 = _mm256_load_ps(&mat[0*8]); 
    r1 = _mm256_load_ps(&mat[1*8]); 
    r2 = _mm256_load_ps(&mat[2*8]); 
    r3 = _mm256_load_ps(&mat[3*8]); 
    r4 = _mm256_load_ps(&mat[4*8]); 
    r5 = _mm256_load_ps(&mat[5*8]); 
    r6 = _mm256_load_ps(&mat[6*8]); 
    r7 = _mm256_load_ps(&mat[7*8]); 

    t0 = _mm256_unpacklo_ps(r0, r1); 
    t1 = _mm256_unpackhi_ps(r0, r1); 
    t2 = _mm256_unpacklo_ps(r2, r3); 
    t3 = _mm256_unpackhi_ps(r2, r3); 
    t4 = _mm256_unpacklo_ps(r4, r5); 
    t5 = _mm256_unpackhi_ps(r4, r5); 
    t6 = _mm256_unpacklo_ps(r6, r7); 
    t7 = _mm256_unpackhi_ps(r6, r7); 

    r0 = _mm256_shuffle_ps(t0,t2,_MM_SHUFFLE(1,0,1,0)); 
    r1 = _mm256_shuffle_ps(t0,t2,_MM_SHUFFLE(3,2,3,2)); 
    r2 = _mm256_shuffle_ps(t1,t3,_MM_SHUFFLE(1,0,1,0)); 
    r3 = _mm256_shuffle_ps(t1,t3,_MM_SHUFFLE(3,2,3,2)); 
    r4 = _mm256_shuffle_ps(t4,t6,_MM_SHUFFLE(1,0,1,0)); 
    r5 = _mm256_shuffle_ps(t4,t6,_MM_SHUFFLE(3,2,3,2)); 
    r6 = _mm256_shuffle_ps(t5,t7,_MM_SHUFFLE(1,0,1,0)); 
    r7 = _mm256_shuffle_ps(t5,t7,_MM_SHUFFLE(3,2,3,2)); 

    t0 = _mm256_permute2f128_ps(r0, r4, 0x20); 
    t1 = _mm256_permute2f128_ps(r1, r5, 0x20); 
    t2 = _mm256_permute2f128_ps(r2, r6, 0x20); 
    t3 = _mm256_permute2f128_ps(r3, r7, 0x20); 
    t4 = _mm256_permute2f128_ps(r0, r4, 0x31); 
    t5 = _mm256_permute2f128_ps(r1, r5, 0x31); 
    t6 = _mm256_permute2f128_ps(r2, r6, 0x31); 
    t7 = _mm256_permute2f128_ps(r3, r7, 0x31); 

    _mm256_store_ps(&matT[0*8], t0); 
    _mm256_store_ps(&matT[1*8], t1); 
    _mm256_store_ps(&matT[2*8], t2); 
    _mm256_store_ps(&matT[3*8], t3); 
    _mm256_store_ps(&matT[4*8], t4); 
    _mm256_store_ps(&matT[5*8], t5); 
    _mm256_store_ps(&matT[6*8], t6); 
    _mm256_store_ps(&matT[7*8], t7); 
} 
*/ 

void tran(float* mat, float* matT) { 
    __m256 r0, r1, r2, r3, r4, r5, r6, r7; 
    __m256 t0, t1, t2, t3, t4, t5, t6, t7; 

    r0 = _mm256_insertf128_ps(_mm256_castps128_ps256(_mm_load_ps(&mat[0*8+0])), _mm_load_ps(&mat[4*8+0]), 1); 
    r1 = _mm256_insertf128_ps(_mm256_castps128_ps256(_mm_load_ps(&mat[1*8+0])), _mm_load_ps(&mat[5*8+0]), 1); 
    r2 = _mm256_insertf128_ps(_mm256_castps128_ps256(_mm_load_ps(&mat[2*8+0])), _mm_load_ps(&mat[6*8+0]), 1); 
    r3 = _mm256_insertf128_ps(_mm256_castps128_ps256(_mm_load_ps(&mat[3*8+0])), _mm_load_ps(&mat[7*8+0]), 1); 
    r4 = _mm256_insertf128_ps(_mm256_castps128_ps256(_mm_load_ps(&mat[0*8+4])), _mm_load_ps(&mat[4*8+4]), 1); 
    r5 = _mm256_insertf128_ps(_mm256_castps128_ps256(_mm_load_ps(&mat[1*8+4])), _mm_load_ps(&mat[5*8+4]), 1); 
    r6 = _mm256_insertf128_ps(_mm256_castps128_ps256(_mm_load_ps(&mat[2*8+4])), _mm_load_ps(&mat[6*8+4]), 1); 
    r7 = _mm256_insertf128_ps(_mm256_castps128_ps256(_mm_load_ps(&mat[3*8+4])), _mm_load_ps(&mat[7*8+4]), 1); 

    t0 = _mm256_unpacklo_ps(r0,r1); 
    t1 = _mm256_unpackhi_ps(r0,r1); 
    t2 = _mm256_unpacklo_ps(r2,r3); 
    t3 = _mm256_unpackhi_ps(r2,r3); 
    t4 = _mm256_unpacklo_ps(r4,r5); 
    t5 = _mm256_unpackhi_ps(r4,r5); 
    t6 = _mm256_unpacklo_ps(r6,r7); 
    t7 = _mm256_unpackhi_ps(r6,r7); 

    __m256 v; 

    //r0 = _mm256_shuffle_ps(t0,t2, 0x44); 
    //r1 = _mm256_shuffle_ps(t0,t2, 0xEE); 
    v = _mm256_shuffle_ps(t0,t2, 0x4E); 
    r0 = _mm256_blend_ps(t0, v, 0xCC); 
    r1 = _mm256_blend_ps(t2, v, 0x33); 

    //r2 = _mm256_shuffle_ps(t1,t3, 0x44); 
    //r3 = _mm256_shuffle_ps(t1,t3, 0xEE); 
    v = _mm256_shuffle_ps(t1,t3, 0x4E); 
    r2 = _mm256_blend_ps(t1, v, 0xCC); 
    r3 = _mm256_blend_ps(t3, v, 0x33); 

    //r4 = _mm256_shuffle_ps(t4,t6, 0x44); 
    //r5 = _mm256_shuffle_ps(t4,t6, 0xEE); 
    v = _mm256_shuffle_ps(t4,t6, 0x4E); 
    r4 = _mm256_blend_ps(t4, v, 0xCC); 
    r5 = _mm256_blend_ps(t6, v, 0x33); 

    //r6 = _mm256_shuffle_ps(t5,t7, 0x44); 
    //r7 = _mm256_shuffle_ps(t5,t7, 0xEE); 
    v = _mm256_shuffle_ps(t5,t7, 0x4E); 
    r6 = _mm256_blend_ps(t5, v, 0xCC); 
    r7 = _mm256_blend_ps(t7, v, 0x33); 

    _mm256_store_ps(&matT[0*8], r0); 
    _mm256_store_ps(&matT[1*8], r1); 
    _mm256_store_ps(&matT[2*8], r2); 
    _mm256_store_ps(&matT[3*8], r3); 
    _mm256_store_ps(&matT[4*8], r4); 
    _mm256_store_ps(&matT[5*8], r5); 
    _mm256_store_ps(&matT[6*8], r6); 
    _mm256_store_ps(&matT[7*8], r7); 
} 


int verify(float *mat) { 
    int i,j; 
    int error = 0; 
    for(i=0; i<8; i++) { 
     for(j=0; j<8; j++) { 
     if(mat[j*8+i] != 1.0f*i*8+j) error++; 
     } 
    } 
    return error; 
} 

void print_mat(float *mat) { 
    int i,j; 
    for(i=0; i<8; i++) { 
     for(j=0; j<8; j++) printf("%2.0f ", mat[i*8+j]); 
     puts(""); 
    } 
    puts(""); 
} 

int main(void) { 
    int i,j, rep; 
    float mat[64] __attribute__((aligned(64))); 
    float matT[64] __attribute__((aligned(64))); 
    double dtime; 

    rep = 10000000; 
    for(i=0; i<64; i++) mat[i] = i; 
    print_mat(mat); 

    tran(mat,matT); 
    //dtime = -omp_get_wtime(); 
    //tran(mat, matT, rep); 
    //dtime += omp_get_wtime(); 
    printf("errors %d\n", verify(matT)); 
    //printf("dtime %f\n", dtime); 
    print_mat(matT); 
} 
+0

AVX512에서 복식으로 사용할 수 있습니다. 8x8 전치 연산자는 2 개의 입력 피연산자로 제한되는 경우 24 개 미만의 연산에서 수행 될 수 없다는 것을 증명할 수 있다고 생각합니다. – Mysticial

+0

@Mysticial, woah, 어떻게 AVX512를 사용하고 있습니까? 제온 피? 에뮬레이터? 나는 부러워. _MM_TRANSPOSE4_PS의 경우 MSVC는 _mm_shuffle_ps를 사용하고 GCC/ICC는 mm_unpacklo_ps/mm_unpackhi_ps/mm_movelh_ps/mm_movehl_ps를 사용합니다. 이것이 왜 있는지 아십니까? –

+2

컴파일 타임 에뮬레이션입니다. 테스트를 위해 AVX 및 FMA3 만 사용하여 AVX512f 내장 함수의 하위 세트를 모방 한 헤더 세트를 가지고 있으므로 Intel 및 AMD 시스템에서 실행할 수 있습니다. (성능면에서는 Haswell에서 실행하는 것이 합리적으로 빠릅니다. 네이티브 AVX2 구현보다 30 % 정도 느립니다.) Knights Landing AVX512f로 컴파일 할 수도 있습니다. 나는 그것을 실행할 수는 없지만, 적어도 어셈블리를 보면 모든 것이 정상적으로 보일 수 있습니다. – Mysticial

6

다음은 8 x 8 32 비트 int에서 작동하는 AVX2 솔루션입니다. 8 x 8 플로트를 조 변경하려면 float 벡터를 int 및 back으로 캐스팅 할 수 있습니다. 또한 수레에만 AVX 전용 버전 (AVX2가 필요하지 않음)을 사용하는 것도 가능하지만 아직 시도하지는 않았습니다.

// 
// tranpose_8_8_avx2.c 
// 

#include <stdio.h> 

#include <immintrin.h> 

#define V_ELEMS 8 

static inline void _mm256_merge_epi32(const __m256i v0, const __m256i v1, __m256i *vl, __m256i *vh) 
{ 
    __m256i va = _mm256_permute4x64_epi64(v0, _MM_SHUFFLE(3, 1, 2, 0)); 
    __m256i vb = _mm256_permute4x64_epi64(v1, _MM_SHUFFLE(3, 1, 2, 0)); 
    *vl = _mm256_unpacklo_epi32(va, vb); 
    *vh = _mm256_unpackhi_epi32(va, vb); 
} 

static inline void _mm256_merge_epi64(const __m256i v0, const __m256i v1, __m256i *vl, __m256i *vh) 
{ 
    __m256i va = _mm256_permute4x64_epi64(v0, _MM_SHUFFLE(3, 1, 2, 0)); 
    __m256i vb = _mm256_permute4x64_epi64(v1, _MM_SHUFFLE(3, 1, 2, 0)); 
    *vl = _mm256_unpacklo_epi64(va, vb); 
    *vh = _mm256_unpackhi_epi64(va, vb); 
} 

static inline void _mm256_merge_si128(const __m256i v0, const __m256i v1, __m256i *vl, __m256i *vh) 
{ 
    *vl = _mm256_permute2x128_si256(v0, v1, _MM_SHUFFLE(0, 2, 0, 0)); 
    *vh = _mm256_permute2x128_si256(v0, v1, _MM_SHUFFLE(0, 3, 0, 1)); 
} 

// 
// Transpose_8_8 
// 
// in place transpose of 8 x 8 int array 
// 

static void Transpose_8_8(
    __m256i *v0, 
    __m256i *v1, 
    __m256i *v2, 
    __m256i *v3, 
    __m256i *v4, 
    __m256i *v5, 
    __m256i *v6, 
    __m256i *v7) 
{ 
    __m256i w0, w1, w2, w3, w4, w5, w6, w7; 
    __m256i x0, x1, x2, x3, x4, x5, x6, x7; 

    _mm256_merge_epi32(*v0, *v1, &w0, &w1); 
    _mm256_merge_epi32(*v2, *v3, &w2, &w3); 
    _mm256_merge_epi32(*v4, *v5, &w4, &w5); 
    _mm256_merge_epi32(*v6, *v7, &w6, &w7); 

    _mm256_merge_epi64(w0, w2, &x0, &x1); 
    _mm256_merge_epi64(w1, w3, &x2, &x3); 
    _mm256_merge_epi64(w4, w6, &x4, &x5); 
    _mm256_merge_epi64(w5, w7, &x6, &x7); 

    _mm256_merge_si128(x0, x4, v0, v1); 
    _mm256_merge_si128(x1, x5, v2, v3); 
    _mm256_merge_si128(x2, x6, v4, v5); 
    _mm256_merge_si128(x3, x7, v6, v7); 
} 

int main(void) 
{ 
    int32_t buff[V_ELEMS][V_ELEMS] __attribute__ ((aligned(32))); 
    int i, j; 
    int k = 0; 

    // init buff 
    for (i = 0; i < V_ELEMS; ++i) 
    { 
     for (j = 0; j < V_ELEMS; ++j) 
     { 
      buff[i][j] = k++; 
     } 
    } 

    // print buff 
    printf("\nBEFORE:\n"); 
    for (i = 0; i < V_ELEMS; ++i) 
    { 
     for (j = 0; j < V_ELEMS; ++j) 
     { 
      printf("%4d", buff[i][j]); 
     } 
     printf("\n"); 
    } 

    // transpose 
    Transpose_8_8((__m256i *)buff[0], (__m256i *)buff[1], (__m256i *)buff[2], (__m256i *)buff[3], (__m256i *)buff[4], (__m256i *)buff[5], (__m256i *)buff[6], (__m256i *)buff[7]); 

    // print buff 
    printf("\nAFTER:\n"); 
    for (i = 0; i < V_ELEMS; ++i) 
    { 
     for (j = 0; j < V_ELEMS; ++j) 
     { 
      printf("%4d", buff[i][j]); 
     } 
     printf("\n"); 
    } 

    // transpose 
    Transpose_8_8((__m256i *)buff[0], (__m256i *)buff[1], (__m256i *)buff[2], (__m256i *)buff[3], (__m256i *)buff[4], (__m256i *)buff[5], (__m256i *)buff[6], (__m256i *)buff[7]); 

    // print buff 
    printf("\nAFTER x2:\n"); 
    for (i = 0; i < V_ELEMS; ++i) 
    { 
     for (j = 0; j < V_ELEMS; ++j) 
     { 
      printf("%4d", buff[i][j]); 
     } 
     printf("\n"); 
    } 

    return 0; 
} 

Transpose_8_8는로드 및 저장을 포함하여 그 소리에 주변에 56 지침을 컴파일 - 내가 좀 더 노력으로이를 개선 할 수 있어야한다고 생각합니다.

컴파일 및 테스트 :

이미이 질문에 Fast memory transpose with SSE, AVX, and OpenMP 대답
$ gcc -Wall -mavx2 -O3 transpose_8_8_avx2.c && ./a.out 

BEFORE: 
    0 1 2 3 4 5 6 7 
    8 9 10 11 12 13 14 15 
    16 17 18 19 20 21 22 23 
    24 25 26 27 28 29 30 31 
    32 33 34 35 36 37 38 39 
    40 41 42 43 44 45 46 47 
    48 49 50 51 52 53 54 55 
    56 57 58 59 60 61 62 63 

AFTER: 
    0 8 16 24 32 40 48 56 
    1 9 17 25 33 41 49 57 
    2 10 18 26 34 42 50 58 
    3 11 19 27 35 43 51 59 
    4 12 20 28 36 44 52 60 
    5 13 21 29 37 45 53 61 
    6 14 22 30 38 46 54 62 
    7 15 23 31 39 47 55 63 

AFTER x2: 
    0 1 2 3 4 5 6 7 
    8 9 10 11 12 13 14 15 
    16 17 18 19 20 21 22 23 
    24 25 26 27 28 29 30 31 
    32 33 34 35 36 37 38 39 
    40 41 42 43 44 45 46 47 
    48 49 50 51 52 53 54 55 
    56 57 58 59 60 61 62 63 

$