2012-06-13 3 views
1

집중적으로 메모리 바인딩 된 코드를 사용하여 작업했습니다. 캐시 블로킹, sw 프리 페치, 루프 언 롤링 등을 수동으로 구현하여 단일 코어 내에서 최적화하려고합니다. 캐시 블로킹이 성능을 크게 향상 시키지만. 그러나 루프 풀기를 도입 할 때 엄청난 성능 저하가 발생합니다.메모리 바인딩 된 데이터에 대한 루프 언 롤링의 영향

필자는 모든 테스트 케이스에서 컴파일러 플래그 -O2 및 -ipo를 사용하여 Intel icc로 컴파일하고 있습니다.

내 코드는 (3D 25 점 스텐실)과 비슷하다

void stencil_baseline (double *V, double *U, int dx, int dy, int dz, double c0, double c1,  double c2, double c3, double c4) 
    { 
    int i, j, k; 

    for (k = 4; k < dz-4; k++) 
    { 
    for (j = 4; j < dy-4; j++) 
    { 
     //x-direction 
      for (i = 4; i < dx-4; i++) 
     { 
      U[k*dy*dx+j*dx+i] = (c0 * (V[k*dy*dx+j*dx+i]) //center 
       + c1 * (V[k*dy*dx+j*dx+(i-1)] + V[k*dy*dx+j*dx+(i+1)])     
       + c2 * (V[k*dy*dx+j*dx+(i-2)] + V[k*dy*dx+j*dx+(i+2)])  
       + c3 * (V[k*dy*dx+j*dx+(i-3)] + V[k*dy*dx+j*dx+(i+3)]) 
       + c4 * (V[k*dy*dx+j*dx+(i-4)] + V[k*dy*dx+j*dx+(i+4)])); 

     } 

     //y-direction 
     for (i = 4; i < dx-4; i++) 
     { 
      U[k*dy*dx+j*dx+i] += (c1 * (V[k*dy*dx+(j-1)*dx+i] + V[k*dy*dx+(j+1)*dx+i]) 
       + c2 * (V[k*dy*dx+(j-2)*dx+i] + V[k*dy*dx+(j+2)*dx+i]) 
       + c3 * (V[k*dy*dx+(j-3)*dx+i] + V[k*dy*dx+(j+3)*dx+i]) 
       + c4 * (V[k*dy*dx+(j-4)*dx+i] + V[k*dy*dx+(j+4)*dx+i])); 
     } 

     //z-direction 
     for (i = 4; i < dx-4; i++) 
     { 
      U[k*dy*dx+j*dx+i] += (c1 * (V[(k-1)*dy*dx+j*dx+i] + V[(k+1)*dy*dx+j*dx+i]) 
       + c2 * (V[(k-2)*dy*dx+j*dx+i] + V[(k+2)*dy*dx+j*dx+i]) 
       + c3 * (V[(k-3)*dy*dx+j*dx+i] + V[(k+3)*dy*dx+j*dx+i]) 
       + c4 * (V[(k-4)*dy*dx+j*dx+i] + V[(k+4)*dy*dx+j*dx+i])); 

     } 

    } 
    } 

} 

I는 루프 안쪽 루프 언 롤링 할 (I 치수)와 방향 (X), (Y)으로 풀어서, Z 별도로 풀다하여 factor 2,4,8 각각 9 가지 경우 모두 성능 저하가 발생합니다. 즉, x 방향으로 2 회전, y 방향으로 2 회전, z 방향으로 2 회전, x 방향으로 4 회전 등 ... 그러나 8 번 요소 (2 & 4)로 가장 바깥 쪽 루프 (차원 k)에서 루프 언 롤링을 수행하면 캐시 차단보다 나은 v.good 성능 향상을 얻습니다.

인텔 Vtune으로 내 코드를 프로파일 링 해 보았습니다. 그것은 주로 병목 현상처럼 보였습니다. 주로 1.LLC Miss 및 2. LLC Load Missed가 원격 DRAM에 의해 처리되었습니다.

왜 가장 안쪽의 가장 빠른 루프를 풀면 성능이 저하되는지 이해할 수 없지만 가장 바깥 쪽의 가장 느린 차원을 풀면 성능이 향상됩니다. 그러나, 후자의 경우에 이러한 개선은 icc로 컴파일 할 때 -O2와 -ipo를 사용할 때입니다.

이 통계를 해석하는 방법을 모르겠습니다. 누군가가이 문제에 대해 밝힐 수 있습니까?

+0

: 컴파일러는 아마도 k * dy * dx를 임시로 최적화합니까? –

+0

@Mitch Wheat : 원본 코드에서 k * dx * dy 및 j * dx를 고려하여 공통 하위 식 제거를 사용하고 있습니다. 더 많은 예제 코드입니다 – Anusuya

+0

SSE 명령어를 사용하여이 중 일부를 계산할 수 있습니까? 빠른 눈길에서보기에 적합합니다. – sarnold

답변

1

이것은 일반적으로 언 롤링에 의한 명령 캐시 누락을 유발할 가능성이 높습니다. 현대 하드웨어 시대에 언 롤링은 더 이상 자동으로 더 빠른 코드를 의미하지 않습니다. 각 내부 루프가 캐시 라인에 맞으면 성능이 향상됩니다.

생성 된 코드의 크기를 제한하기 위해 수동으로 언 롤링 할 수는 있지만 생성 된 기계어 명령어와 그 위치를 검사하여 루프가 단일 캐시 라인 내에 있는지 확인해야합니다 . 캐시 라인은 일반적으로 64 바이트 길이이며 64 바이트 경계에 정렬됩니다.

외부 루프에는 동일한 효과가 없습니다. 언롤 수준에 관계없이 명령 캐시 외부에있을 가능성이 큽니다. 브랜치 수를 줄이면 결과가 언 롤링되므로 성능이 향상됩니다.

"원격 DRAM에서로드 미스로드"는 하나의 NUMA 노드에 메모리를 할당했음을 의미하지만 이제는 다른 NUMA 노드에서 실행 중임을 나타냅니다. NUMA 기반의 프로세스 또는 스레드 선호도 설정이 그 해답입니다.

원격 DRAM은 내가 사용한 Intel 컴퓨터에서 로컬 DRAM으로 읽는 데 거의 두 배의 시간이 걸립니다.