함수 의존성 openMP

Nov 24 2020

5 가지 기능 A, B, C, D, E가 있습니다.

DI를 실행하려면 B, C가 실행되고 EI를 실행하려면 A, D가 실행되어야합니다.

나는 이것을 시도했다

int main()
{
    #pragma omp parallel num_threads(5) 
    {
        long t1 = clock();
        int a = 0, b = 0, c = 0, d = 0, e = 0;
        int th = omp_get_thread_num();
        if (th == 0) {
            a += A();
            printf("A is finished after %d\n", clock() - t1);
        }
        if (th == 1) {
            b += B();
            printf("B is finished after %d\n", clock() - t1);
        }
        if (th == 2) {
            c += C();
            printf("C is finished after %d\n", clock() - t1);
        }
        if (th == 3 && (b == 1 && c == 1)) {
            d += D();
            printf("D is finished after %d\n", clock() - t1);
        }
        if (th == 4 && (a == 1 && d == 1)) {
            e += E();
            printf("E is finished after %d\n", clock() - t1);
        }

    }
    return 0;
}

하지만 D, E는 실행하지 않았습니다.

이 모든 함수는 디버깅 목적으로 지금까지 1을 반환합니다.

답변

1 dreamcrash Nov 24 2020 at 16:58

변수는 a, b, cd,사용할 수 없습니다 통신 그들은 모두 있기 때문에 스레드간에 개인 . 따라서 각 스레드에는 자체 개인 사본이 있습니다. 또한 일반적으로 동기화 목적으로 사용하는 것은 좋지 않습니다.

코드 thread=3에서 다음과 같은 if (th == 3 && (b == 1 && c == 1))이유로 기다리지 않을 것입니다 .

  1. b그리고 c그렇게, 사립 thread=3b=0c=0관계없이 다른 스레드가 자신에게 한 일의 사본 변수 b=0c=0.
  2. 아무것도 말하고 스레드 (대기하는 것을 예를 들어, 일부 동기화 모두 생성자).

스레드가 서로를 기다리게하려면 omp barrier대신 사용하십시오 . 모든 스레드는 계산을 진행하기 전에 장벽을 호출해야합니다.

 int main()
    {
        #pragma omp parallel num_threads(5) 
        {
            long t1 = clock();
            int a = 0, b = 0, c = 0, d = 0, e = 0;
            int th = omp_get_thread_num();
            if (th == 0) {
                a += A();
                printf("A is finished after %d\n", clock() - t1);
            }
            if (th == 1) {
                b += B();
                printf("B is finished after %d\n", clock() - t1);
            }
            if (th == 2) {
                c += C();
                printf("C is finished after %d\n", clock() - t1);
            }
            // Threads will wait for each other here
            #pragma omp barrier 
            if (th == 3) {
                d += D();
                printf("D is finished after %d\n", clock() - t1);
            }
            // Threads will wait for each other here
            #pragma omp barrier 
            if (th == 4) {
                e += E();
                printf("E is finished after %d\n", clock() - t1);
            }
        }
        return 0;
    }

보다 정교한 접근 방식은 OpenMP 4.0 표준에 릴리스 된 기능과 종속성이있는 작업을 사용하는 것입니다. 이 스레드 에서이 기능이 어떻게 작동하는지에 대한 멋진 설명이 이미 있습니다 .

int a = 0, b = 0, c = 0, d = 0, e = 0;
#pragma omp parallel num_threads(5) shared(a, b, c, d)
{
  #pragma omp single nowait
  {
      long t1 = clock();

      int th = omp_get_thread_num();
      #pragma omp task  depend (out:a) 
      {
          a += A();
          printf("A is finished after %d\n", clock() - t1);
      }
      #pragma omp task depend (out:b) 
      {
         b += B();
         printf("B is finished after %d\n", clock() - t1);
      }
      #pragma omp task depend (out:c) 
      { 
          c += C();
          printf("C is finished after %d\n", clock() - t1);
      }
     #pragma omp task depend (in:a, b) depend(out:d) 
     {
        d += D(); 
        printf("D is finished after %d\n", clock() - t1);
     }
     #pragma omp task depend (in:a, b)  
     {
       e += E();
       printf("E is finished after %d\n", clock() - t1);
     }
  }
}
return 0;
}
2 HristoIliev Nov 24 2020 at 17:43

적절한 OpenMP 솔루션은 데이터 종속성이있는 작업을 사용하는 것입니다.

    #pragma omp parallel num_threads(3)
    #pragma omp single
    {
        double t1 = omp_wtime();
        int a = 0, b = 0, c = 0, d = 0, e = 0;
        #pragma omp task shared(a) depend(out: a)
        {
            a += A();
            printf("A is finished after %f\n", omp_wtime() - t1);
        }
        #pragma omp task shared(b) depend(out: b)
        {
            b += B();
            printf("B is finished after %f\n", omp_wtime() - t1);
        }
        #pragma omp task shared(c) depend(out: c)
        {
            c += C();
            printf("C is finished after %f\n", omp_wtime() - t1);
        }
        #pragma omp task shared(b,c,d) depend(in: b,c) depend(out: d)
        {
            d += D();
            printf("D is finished after %f\n", omp_wtime() - t1);
        }
        #pragma omp task shared(a,d,e) depend(in: a,d)
        {
            e += E();
            printf("E is finished after %f\n", omp_wtime() - t1);
        }

    }

여기서 task Aawith 값에 대해 생산자로 표시 depend(out: a)되고 task Ddwith 의 생산자로 표시됩니다 depend(out: d). 작업 E은이 두 값의 소비자로 표시됩니다 depend(in: a,d). 출력 (생산자) 및 입력 (소비자) 종속성에 따라 OpenMP 런타임은 모든 작업의 ​​적절한 실행 순서를 알려주는 실행 DAG (방향성 비순환 그래프)를 빌드합니다. 또한 5 개의 스레드가 필요하지 않습니다. 3 개이면 충분합니다.

single구문 내부에 태스크 코드를 포함하는 것은 매우 관용적 인 OpenMP입니다.

작업 종속성은 2013 년에 OpenMP 4.0에서 도입되었으므로 MSVC ++를 제외한 모든 최신 컴파일러는 해당 기능에 대한 지원을 제공해야합니다.