7

我知道 OpenMP 实际上只是一组编译成 pthread 的宏。有没有办法在编译的其余部分发生之前查看 pthread 代码?我正在使用 GCC 进行编译。

4

3 回答 3

12

首先,OpenMP不是一组简单的宏。可以看到将其简单地转换为类似 pthread 的代码,但 OpenMP 确实需要的不仅仅是运行时支持。

回到您的问题,至少在 GCC 中,您看不到 pthread 代码,因为 GCC 的 OpenMP 实现是在编译器后端(或中间端)中完成的。转换是在 IR(中间表示)级别完成的。因此,从程序员的角度来看,代码实际上是如何转换的并不容易。

不过,也有一些参考。

(1) 一位英特尔工程师对在英特尔 C/C++ 编译器中实现 OpenMP 进行了很好的概述:

http://www.drdobbs.com/parallel/how-do-openmp-compilers-work-part-1/226300148

http://www.drdobbs.com/parallel/how-do-openmp-compilers-work-part-2/226300277

(2)大家可以看一下GCC的OpenMP的实现:

https://github.com/mirrors/gcc/tree/master/libgomp

Seelibgomp.h确实使用 pthread,并loop.c包含并行循环构造的实现。

于 2013-02-14T18:27:25.093 回答
6

OpenMP 是一组编译器指令,而不是宏。在 C/C++ 中,这些指令是通过#pragma扩展机制实现的,而在 Fortran 中,它们是作为特殊格式的注释实现的。这些指令指示编译器执行某些代码转换,以便将串行代码转换为并行代码。

尽管可以将 OpenMP 实现为纯 pthreads 代码的转换,但很少这样做。OpenMP 机制的大部分通常内置在单独的运行时库中,该库作为编译器套件的一部分提供。对于 GCC,这是libgomp. 它提供了一组用于轻松实现 OpenMP 结构的高级函数。它也是编译器内部的,不打算供用户代码使用,即没有提供头文件。

使用 GCC,可以获得 OpenMP 转换后代码外观的伪代码表示。您必须为其提供-fdump-tree-all选项,这将导致编译器为每个编译单元生成大量中间文件。最有趣的是filename.017t.ompexp(来自 GCC 4.7.1,其他 GCC 版本的数字可能不同,但扩展名仍然是.ompexp)。该文件包含 OpenMP 结构被降低然后扩展为正确实现之后的代码中间表示。

考虑以下示例 C 代码,另存为fun.c

void fun(double *data, int n)
{
   #pragma omp parallel for
   for (int i = 0; i < n; i++)
     data[i] += data[i]*data[i];
}

的内容fun.c.017t.ompexp是:

fun (double * data, int n)
{
  ...
  struct .omp_data_s.0 .omp_data_o.1;
  ...

<bb 2>:
  .omp_data_o.1.data = data;
  .omp_data_o.1.n = n;
  __builtin_GOMP_parallel_start (fun._omp_fn.0, &.omp_data_o.1, 0);
  fun._omp_fn.0 (&.omp_data_o.1);
  __builtin_GOMP_parallel_end ();
  data = .omp_data_o.1.data;
  n = .omp_data_o.1.n;
  return;
}

fun._omp_fn.0 (struct .omp_data_s.0 * .omp_data_i)
{
  int n [value-expr: .omp_data_i->n];
  double * data [value-expr: .omp_data_i->data];
  ...

<bb 3>:
  i = 0;
  D.1637 = .omp_data_i->n;
  D.1638 = __builtin_omp_get_num_threads ();
  D.1639 = __builtin_omp_get_thread_num ();
  ...

<bb 4>:
  ... this is the body of the loop ...
  i = i + 1;
  if (i < D.1644)
    goto <bb 4>;
  else
    goto <bb 5>;

<bb 5>:

<bb 6>:
  return;

  ...
}

为简洁起见,我省略了大部分输出。这不完全是 C 代码。它是程序流的类 C 表示。<bb N>是所谓的基本块- 语句的集合,在程序的工作流程中被视为单个块。人们看到的第一件事是并行区域被提取到一个单独的函数中。这并不少见——大多数 OpenMP 实现或多或少都进行了相同的代码转换。还可以观察到编译器插入了对和之类的函数的调用,这些libgomp函数用于引导然后完成并行区域的执行(稍后将删除前缀)。里面有一个循环,实现在GOMP_parallel_startGOMP_parallel_end__builtin_fun._omp_fn.0for<bb 4>(注意循环本身也被扩展了)。此外,所有共享变量都被放入一个特殊的结构中,该结构被传递给并行区域的实现。<bb 3>包含计算当前线程将运行的迭代范围的代码。

嗯,不完全是 C 代码,但这可能是最接近 GCC 的东西。

于 2013-02-14T19:20:00.217 回答
-1

我还没有用openmp测试过。但是编译器选项-E应该在预处理后为您提供代码。

于 2013-02-14T16:32:15.157 回答