据我所知,OpenMP实际上只是一组编译成pthread的宏。有没有办法在编译的其余部分之前看到pthread代码?我正在使用GCC进行编译。
答案 0 :(得分:10)
首先,OpenMP 不是一组简单的宏。可以看到一个简单的转换为类似pthread的代码,但是OpenMP确实需要的不仅仅包括运行时支持。
回到你的问题,至少在GCC中,你看不到pthreaded代码,因为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
请参阅libgomp.h
确实使用pthread,loop.c
包含并行循环结构的实现。
答案 1 :(得分:5)
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_start
和GOMP_parallel_end
,它们用于引导,然后完成并行区域的执行({{1}稍后删除前缀)。在__builtin_
内部有一个fun._omp_fn.0
循环,在for
中实现(请注意,循环本身也会展开)。此外,所有共享变量都被放入一个特殊的结构中,并传递给并行区域的实现。 <bb 4>
包含用于计算当前线程将在其上运行的迭代范围的代码。
嗯,不是一个C代码,但这可能是最接近GCC的东西。
答案 2 :(得分:-1)
我还没有用openmp测试它。但编译器选项-E
应该在预处理后为您提供代码。