首页 > 解决方案 > 为什么同一函数的第二次调用永远执行?

问题描述

我写了一个函数,它使用 aparallel for用静态计划做一些计算,然后它返回到我的 main.js 中。之后,我再次调用这个函数,但这次它永远运行,所以我不得不中止程序。

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <omp.h>
#include <time.h>

int thread_count;

void work(int x) {
    int divisor = 0;
    for (int i=1; i<=x; i++) {
        if ((x%i) == 0) {
            divisor++;
        }
    }
}

void initialize(int *codes, int n) {
    thread_count = 4;

    srand(time(NULL));
    for (int i=0; i<n; i++) {
        codes[i] = rand() % 10000;
    }
}

double get_difference(double *times, int n) {
    double min, max;
    min = max = times[0];
    for (int i=1; i<n; i++) {
        if (times[i] > max) {
            max = times[i];
        }
        if (times[i] < min) {
            min = times[i];
        }
    }
    return (max-min);
}

void my_function(int *a, double *times, int n, int thread_count) {
    long i;
    #pragma omp parallel
    {
      #pragma omp parallel for num_threads(thread_count) \
        shared(a, n) private(i) schedule(static, 1)
        for (i=0; i<n; i++) {
            work(a[i]);
        }
        double wtime = omp_get_wtime();
        printf( "Time taken by thread %d is %f\n", omp_get_thread_num(), wtime);
        times[omp_get_thread_num()] = wtime;
     }
}

void odd_even(int *a, int n) {
   int phase, i, tmp;

   # pragma omp parallel num_threads(thread_count) \
      default(none) shared(a, n) private(i, tmp, phase)
   for (phase = 0; phase < n; phase++) {
      if (phase % 2 == 0)
        # pragma omp for 
         for (i = 1; i < n; i += 2) {
            if (a[i-1] < a[i]) {
               tmp = a[i-1];
               a[i-1] = a[i];
               a[i] = tmp;
            }
         }
      else
         #pragma omp for 
         for (i = 1; i < n-1; i += 2) {
            if (a[i] < a[i+1]) {
               tmp = a[i+1];
               a[i+1] = a[i];
               a[i] = tmp;
            }
         }
   }
}

在我的主要工作中,我会打电话:

int main(int argc, char *argv[]) {
    int n = atoi(argv[1]);
    int arr[n];
    double times[thread_count];
    initialize(arr, n);
    odd_even(arr, n);
    my_function(arr, times, n, thread_count);
    double difference = get_difference(times, thread_count);
    printf("Difference is %f\n", difference);

    // my_function(arr, times, n, thread_count);
    // difference = get_difference(times, thread_count);
    // printf("Difference is %f\n", difference);
}

我对标准输出进行了一些打印,它会在几秒钟内为第一次调用顺利打印每个线程的时间戳,但是当我进行第二次调用时,程序将永远执行并且没有任何打印。

我尝试了调度块大小为 n/thread_count 的块分布和块大小为 1 的块循环分布,但无论哪种方式我都遇到了同样的问题。

我还尝试复制该函数并一个接一个地调用具有相同内容的两个不同函数,但这也不起作用。

我没有更改两次调用之间的任何变量和数据,那么为什么第二个函数调用没有正确执行?

标签: cmultithreadingperformanceparallel-processingopenmp

解决方案


您的代码存在一些问题,在函数my_function中,循环的迭代没有按照您的意愿分配给线程。因为您再次将子句添加parallel#pragma omp for,并假设您已禁用嵌套并行性(默认情况下),所以在外部并行区域中创建的每个线程都将“按顺序”执行该区域内的代码。因此,对于 an = 6number of threads = 4,您将拥有以下代码块:

for (i=0; i<n; i++) {
    work(a[i]);
}

正在执行6 x 4=24次(循环迭代的总数乘以线程总数)。如需更深入的解释,请查看有关类似问题的SO Thread 。然而,下图提供了基本要素的可视化:

在此处输入图像描述

所以修复my_function到:

void my_function(int *a, double *times, int n, int thread_count) {
    # pragma omp parallel num_threads(thread_count) shared(a)
    {
       #pragma omp for schedule(static, 1)
       for (long i=0; i<n; i++) {
           work(a[i]);
       }
    double wtime = omp_get_wtime();
    printf( "Time taken by thread %d is %f\n", omp_get_thread_num(), wtime);
    times[omp_get_thread_num()] = wtime;
    }
}

thread_count其次,在正确初始化之前使用该变量:

double times[thread_count];
initialize(arr, n);

改成 :

initialize(arr, n);
double times[thread_count];

最后一个问题是导致未定义的行为,这可能导致无法预料的问题。

另一个您可能知道或不知道的问题是具有讽刺意味的是,该功能work实际上并没有做任何有意义的事情

单独调用 double wtime = omp_get_wtime();不会告诉您线程工作了多长时间。根据 OpenMP 文档

omp_get_wtime 例程以秒为单位返回经过的挂钟时间。

因此,要衡量在某些代码块中花费的时间,您可以执行以下操作

double begin = omp_get_wtime();
// block of code
double end = omp_get_wtime();

并使用表达式end-begin以秒为单位获取时间。在你的情况下:

 void my_function(int *a, double *times, int n, int thread_count) {
        # pragma omp parallel num_threads(thread_count) shared(a)
        {
           double begin = omp_get_wtime();
           #pragma omp for schedule(static, 1)
           for (long i=0; i<n; i++) {
               work(a[i]);
           }
           double end = omp_get_wtime();
           double time = end - begin;
           printf( "Time taken by thread %d is %f\n", omp_get_thread_num(), time);
           times[omp_get_thread_num()] = time;
        }
    }

推荐阅读