【问题标题】:How to execute/skip certain lines of code using preprocessor in C?如何使用 C 中的预处理器执行/跳过某些代码行?
【发布时间】:2013-12-04 11:13:30
【问题描述】:

我有一个用于计算 N 皇后的简单 C 程序。我使用 OpenMP 将其并行化。现在我想同时执行串行和并行版本并计算加速。关键是我不想为串行代码创建一个新文件,或者只是将我的解决方案复制到一个没有 OpenMP 指令的新函数。我想要做的是,保留一个功能,并从 main 告诉它何时以串行方式执行,何时以并行方式执行。我虽然使用预处理器,但我确定它是否可能,如果是,我该如何实现它。

void solve() 
{
    int i;

    #if PARALLEL == 1
      #pragma omp parallel for
    #endif
    for(i = 0; i < size; i++) {
        int *queens = (int*)malloc(sizeof(int)*size);
        setQueen(queens, 0, i);
        free(queens);
    }
}


int main()
{
   ...

    #define PARALLEL 0
    st_start = clock();
    solve();
    st_end = clock();

    #define PARALLEL 1
    pt_start = omp_get_wtime();
    solve();
    pt_end = omp_get_wtime();

    ...
}

【问题讨论】:

    标签: c parallel-processing openmp c-preprocessor n-queens


    【解决方案1】:

    编辑:我想到了一种使用预处理器的方法。这以使编译和链接稍微复杂一些为代价解决了重复代码的问题。它使用的功能是,如果在编译器中未启用 OpenMP,则会忽略 OpenMP 结构。

    #include <stdlib.h>
    
    void setQueen(int* x, int y, int z) {
       /*code*/
    }
    #if defined _OPENMP
    void solve_parallel(const int size)
    #else
    void solve_serial(const int size)
    #endif
    {
        int i;
        #pragma omp parallel for      
        for(i = 0; i < size; i++) {
            int *queens = (int*)malloc(sizeof(int)*size);
            setQueen(queens, 0, i);
            free(queens);
        }
    }
    

    编译

    gcc -O3 -c foo.c -o solve_serial
    gcc -O3 -fopenmp -c foo.c solve_parallel
    

    然后您可以在solve_serial 和solve_parallel 对象文件中使用类似于下面的函数指针和链接的主函数。

    另一种选择是像这样传递线程数:

    void solve(const int nthreads)
    {
        int i;
        const int size = 10;
        #pragma omp parallel for num_threads(nthreads)
        for(i = 0; i < size; i++) {
            int *queens = (int*)malloc(sizeof(int)*size);
            setQueen(queens, 0, i);
            free(queens);
        }
    }
    

    但是,即使对于 nthreads=1,编译器也必须插入 OpenMP 结构,与不使用 OpenMP 编译相比,这会降低性能,因此会产生有偏差的比较。

    更公平的解决方案是定义两个带有和不带有 OpenMP 的函数,然后使用函数指针数组(见下文)。当您想要比较一个函数的多个变体以进行优化时,这会更有用。

    #include <stdlib.h>
    #include <omp.h>
    void solve_parallel(const int size)
    {
        int i;
        #pragma omp parallel for
        for(i = 0; i < size; i++) {
            int *queens = (int*)malloc(sizeof(int)*size);
            setQueen(queens, 0, i);
            free(queens);
        }
    }
    
    void solve_serial(const int size)
    {
        int i;
        for(i = 0; i < size; i++) {
            int *queens = (int*)malloc(sizeof(int)*size);
            setQueen(queens, 0, i);
            free(queens);
        }
    }
    
    int main(void) {
        const int size = 100;
        int i;
        double dtime[2];
        void (*solve[2])(int);
    
        solve[0] = solve_serial;
        solve[1] = solve_parallel;
    
        solve[1](size);  /* run OpenMP once to warm it up */
    
        for(i=0; i<2; i++) {
            dtime[i] = omp_get_wtime();
            solve[i](size);
            dtime[i] = omp_get_wtime() - dtime[i];
        }
    
        return 0;
    }
    

    【讨论】:

      【解决方案2】:

      不幸的是,你不能那样做。

      预处理器只是扫描您的代码并替换#stuff。 完成后,编译器编译代码,#this 没有任何内容

      所以在您发布的代码中,预处理器从第一行开始,如果 PARALLEL 为 1,则执行 #pragma 代码,然后在 main 处继续,将 PARALLEL 定义为 0,然后定义为 1。

      它不是从 main 开始然后进入solve();

      你可能想看看OpenMP: conditional use of #pragma

      你可以试试

      void solve(int paral) 
      {
          int i;
      
      
          #pragma omp parallel for if (paral == 1)
      
          for(i = 0; i < size; i++) {
              int *queens = malloc(sizeof(int)*size);
              setQueen(queens, 0, i);
              free(queens);
          }
      }
      

      我没有尝试过这段代码,也没有使用 OMP 的经验……

      【讨论】:

      • 还有其他方法吗?
      • 实际上#pragmas 不会被预处理器取代。还有please don't recommend casting the return value of malloc() in C.
      • 我已经添加了我发现的内容,我认为这些内容适用于您的情况。 @unwind 感谢您的反馈。
      • if 语句仍将 OpenMP 构造插入代码中(因为它是在运行时而非编译时解析的)。这将使串行代码产生偏差。编译器也无法优化代码。公平比较并行代码和串行代码的唯一方法是定义两个单独的函数。
      • 请参阅 Shahbaz 在may-compiler-optimizations-be-inhibited-by-multi-threading 上的回答,了解为什么 if 语句仍会插入 OpenMP 结构并且会限制优化。
      【解决方案3】:

      调用预处理器,这是编译过程的第一部分;就这样,所有的包含都被解决了,所有的前导指令都被解决了,常量被它们的值替换等等......

      因此,您不能使用预处理器指令来做出运行时决定,而只能做出编译时决定。

      【讨论】:

        猜你喜欢
        • 2021-11-08
        • 2015-12-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-02-03
        • 2019-06-29
        • 1970-01-01
        相关资源
        最近更新 更多