2012-03-13 2 views
2

C++ AMP를 사용하여 병렬 컴퓨팅을위한 알고리즘 (Lattice Boltzmann)을 최적화하려고합니다. 그리고 메모리 레이아웃을 최적화하기위한 몇 가지 제안을 찾고, 구조에서 하나의 매개 변수를 다른 벡터 (차단 된 벡터)로 제거하면 약 10 %가 증가한다는 것을 알았습니다.병렬 컴퓨팅을위한 메모리 레이아웃 개선

누구나 개선 할 수있는 팁이 있습니까? 아니면 고려해야 할 사항이 있습니까? 다음은 각 timestep에 대해 실행되는 가장 시간이 많이 걸리는 기능과 레이아웃에 사용 된 구조입니다.

struct grid_cell { 
// int blocked; // Define if blocked 
    float n;  // North 
    float ne;  // North-East 
    float e;  // East 
    float se;  // South-East 
    float s; 
    float sw; 
    float w; 
    float nw; 
    float c;  // Center 
}; 

int collision(const struct st_parameters param, vector<struct grid_cell> &node, vector<struct grid_cell> &tmp_node, vector<int> &obstacle) { 
    int x,y; 
    int i = 0; 
    float c_sq = 1.0f/3.0f;  // Square of speed of sound 
    float w0 = 4.0f/9.0f;  // Weighting factors 
    float w1 = 1.0f/9.0f; 
    float w2 = 1.0f/36.0f; 

    int chunk = param.ny/20; 
    float total_density = 0; 

    float u_x,u_y;    // Avrage velocities in x and y direction 
    float u[9];     // Directional velocities 
    float d_equ[9];    // Equalibrium densities 
    float u_sq;     // Squared velocity 
    float local_density;  // Sum of densities in a particular node 

    for(y=0;y<param.ny;y++) { 
     for(x=0;x<param.nx;x++) { 
      i = y*param.nx + x; // Node index 
      // Dont consider blocked cells 
      if (obstacle[i] == 0) { 
       // Calculate local density 
       local_density = 0.0; 
       local_density += tmp_node[i].n; 
       local_density += tmp_node[i].e; 
       local_density += tmp_node[i].s; 
       local_density += tmp_node[i].w; 
       local_density += tmp_node[i].ne; 
       local_density += tmp_node[i].se; 
       local_density += tmp_node[i].sw; 
       local_density += tmp_node[i].nw; 
       local_density += tmp_node[i].c; 

       // Calculate x velocity component 
       u_x = (tmp_node[i].e + tmp_node[i].ne + tmp_node[i].se - 
         (tmp_node[i].w + tmp_node[i].nw + tmp_node[i].sw)) 
        /local_density; 
       // Calculate y velocity component 
       u_y = (tmp_node[i].n + tmp_node[i].ne + tmp_node[i].nw - 
         (tmp_node[i].s + tmp_node[i].sw + tmp_node[i].se)) 
        /local_density; 
       // Velocity squared 
       u_sq = u_x*u_x +u_y*u_y; 

       // Directional velocity components; 
       u[1] = u_x;  // East 
       u[2] =  u_y; // North 
       u[3] = -u_x;  // West 
       u[4] =  - u_y; // South 
       u[5] = u_x + u_y; // North-East 
       u[6] = -u_x + u_y; // North-West 
       u[7] = -u_x - u_y; // South-West 
       u[8] = u_x - u_y; // South-East 

       // Equalibrium densities 
       // Zero velocity density: weight w0 
       d_equ[0] = w0 * local_density * (1.0f - u_sq/(2.0f * c_sq)); 
       // Axis speeds: weight w1 
       d_equ[1] = w1 * local_density * (1.0f + u[1]/c_sq 
           + (u[1] * u[1])/(2.0f * c_sq * c_sq) 
           - u_sq/(2.0f * c_sq)); 
       d_equ[2] = w1 * local_density * (1.0f + u[2]/c_sq 
           + (u[2] * u[2])/(2.0f * c_sq * c_sq) 
           - u_sq/(2.0f * c_sq)); 
       d_equ[3] = w1 * local_density * (1.0f + u[3]/c_sq 
           + (u[3] * u[3])/(2.0f * c_sq * c_sq) 
           - u_sq/(2.0f * c_sq)); 
       d_equ[4] = w1 * local_density * (1.0f + u[4]/c_sq 
           + (u[4] * u[4])/(2.0f * c_sq * c_sq) 
           - u_sq/(2.0f * c_sq)); 
       // Diagonal speeds: weight w2 
       d_equ[5] = w2 * local_density * (1.0f + u[5]/c_sq 
           + (u[5] * u[5])/(2.0f * c_sq * c_sq) 
           - u_sq/(2.0f * c_sq)); 
       d_equ[6] = w2 * local_density * (1.0f + u[6]/c_sq 
           + (u[6] * u[6])/(2.0f * c_sq * c_sq) 
           - u_sq/(2.0f * c_sq)); 
       d_equ[7] = w2 * local_density * (1.0f + u[7]/c_sq 
           + (u[7] * u[7])/(2.0f * c_sq * c_sq) 
           - u_sq/(2.0f * c_sq)); 
       d_equ[8] = w2 * local_density * (1.0f + u[8]/c_sq 
           + (u[8] * u[8])/(2.0f * c_sq * c_sq) 
           - u_sq/(2.0f * c_sq)); 

       // Relaxation step 
       node[i].c = (tmp_node[i].c + param.omega * (d_equ[0] - tmp_node[i].c)); 
       node[i].e = (tmp_node[i].e + param.omega * (d_equ[1] - tmp_node[i].e)); 
       node[i].n = (tmp_node[i].n + param.omega * (d_equ[2] - tmp_node[i].n)); 
       node[i].w = (tmp_node[i].w + param.omega * (d_equ[3] - tmp_node[i].w)); 
       node[i].s = (tmp_node[i].s + param.omega * (d_equ[4] - tmp_node[i].s)); 
       node[i].ne = (tmp_node[i].ne + param.omega * (d_equ[5] - tmp_node[i].ne)); 
       node[i].nw = (tmp_node[i].nw + param.omega * (d_equ[6] - tmp_node[i].nw)); 
       node[i].sw = (tmp_node[i].sw + param.omega * (d_equ[7] - tmp_node[i].sw)); 
       node[i].se = (tmp_node[i].se + param.omega * (d_equ[8] - tmp_node[i].se)); 
      } 
     } 
    } 
    return 1; 
} 

답변

6

현재 GPU는 메모리 레이아웃에 따라 많은 것으로 알려져 있습니다. 응용 프로그램에 대한 자세한 내용은 여기에 몇 가지가 없으면 나는 당신이 탐험 제안 : GPU는 "구조의 배열"에서 "배열의 구조체를"선호 있도록

  1. 단위 - 보폭의 접근이 매우 중요합니다. "blocked"필드를 "obstacle"벡터로 이동 시키면 "grid_cell"의 모든 필드를 별도의 벡터로 변환하는 것이 유리합니다. 이렇게하면 모든 필드에 액세스하지 않는 루프에 대해서도 CPU에 이점이 나타납니다.

  2. "장애물"이 매우 희박한 경우 (내가 추측 할 것 같지 않은 경우) 그 자체의 벡터로 이동하는 것이 특히 중요합니다. CPU와 같은 GPU는 캐시 라인이나 다른 형식의 메모리 시스템에서 하나 이상의 단어를로드하며 일부 데이터가 필요하지 않을 때 대역폭을 낭비합니다. 많은 시스템 메모리 대역폭은 병목 자원이므로 대역폭을 줄이는 방법이 도움이됩니다.

  3. 이 더 투기,하지만 지금은 출력 벡터를 모두 작성하는 것으로,이 메모리 서브 시스템은 단순히 어떤 시스템

  4. 덮어 쓰게됩니다 "노드"에서 읽기 값을 피하는 것이 가능하다, 온칩 메모리가 뱅크로 분할되고 구조 내의 홀수 필드가 있으면 은행 충돌을 제거하는 데 도움이 될 수 있습니다.

  5. 일부 시스템은로드 및 저장을 "벡터화"하므로 구조에서 "차단됨"을 제거하면 더 많은 벡터화가 가능해질 수 있습니다. 구조체 배열로의 전환은 이러한 걱정을 완화합니다.

C++ AMP에 관심을 가져 주셔서 감사합니다.

데이비드 칼라

http://blogs.msdn.com/b/nativeconcurrency/ C++ AMP 팀 블로그

7

일반적으로, 다른 CPU에 사용되는 데이터는 (쉬운) 공유되지 않도록해야하고 같은 캐시 라인에없는 (: False Sharing is No Fun를 거짓 공유, 여기에 예를 들어, 참조). 동일한 CPU에서 사용되는 데이터는 캐시를 활용할 수 있도록 서로 가까이 있어야합니다.

1

일부 작은 일반적인 정상은 :

  • 다중 프로세서에서 공유되는 모든 데이터 구조는 읽기 전용해야합니다.

  • 수정이 필요한 데이터 구조는 프로세서마다 고유하며 다른 프로세서에서 필요로하는 데이터와 메모리 위치를 공유하지 않습니다.

  • 코드가 순차적으로 스캔되도록 메모리가 정렬되어 있는지 확인하십시오 (거대한 단계를 거치지 않고 뛰어 넘지 않음).

관련 문제