将C++ 11 thread_local与其他并行库一起使用

Yan*_*hou 5 tbb openmp thread-safety thread-local c++11

我有一个简单的问题,C++ 11 thread_local可以与其他并行模型一起使用.

例如,我可以在使用OpenMP或Intel TBB并行执行任务时在函数内使用它.

大多数此类并行编程模型隐藏了更高级API背后的硬件线程.我的直觉是他们都必须将他们的任务调度程序映射到硬件线程中.我能指望C++ 11 thread_local会产生预期的效果吗?

一个简单的例子是,

void func ()
{
    static thread_local some_var = init_val;
#pragma omp parallel for [... clauses ...]
    for (int i = 0; i < N; ++i) {
        // access some_var somewhere within the loop
    }
}
Run Code Online (Sandbox Code Playgroud)

我可以期望每个OpenMP线程都可以访问自己的副本some_var吗?

我知道大多数并行编程模型都有自己的线程局部存储结构.但是,能够使用C++ 11 thread_local(或编译器特定的关键字)是很好的.例如,考虑一下情况

// actually may implemented with a class with operator()
void func ()
{
     static thread_local some_var;
     // a quite complex function
}

void func_omp (int N)
{
#pragma omp for [... clauses ...]
    for (int i = 0; i < N; ++i)
        func();
}

void func_tbb (int N)
{
      tbb::parallel_for(tbb::blocked_range<int>(0, N), func);
}

void func_select (int N)
{
     // At runtime or at compile time, based which programming model is available,
     // select to run func_omp or func_tbb
}
Run Code Online (Sandbox Code Playgroud)

这里的基本想法func可能相当复杂.我想支持多个并行编程模型.如果我使用并行编程特定的线程局部构造,那么我实现了不同版本func或至少部分版本.但是,如果我可以自由地使用C++ 11 thread_local,那么除了func我之外我只需要实现一些非常简单的函数.而对于大型项目的东西可以用模板写的更通用的版本进一步简化func_omp,func_tbb.但是,我不太确定这样做是否安全.

Hri*_*iev 9

一方面,OpenMP规范有意省略了与其他编程范例的互操作性的任何规范,并且C++ 11线程与OpenMP的任何混合都是非标准的和特定于供应商的.另一方面,编译器(至少是GCC)倾向于使用相同的底层TLS机制来实现OpenMP #pragma omp threadprivate,C++ 11 thread_local和各种编译器特定的存储类__thread.

例如,GCC完全在POSIX线程API之上实现其OpenMP运行时(libgomp),并threadprivate通过将变量放在ELF TLS存储上来实现OpenMP .这与GNU的C++ 11实现互操作,该实现也使用POSIX线程并将thread_local变量放在ELF TLS存储上.最终,这与使用__thread关键字指定线程本地存储类和显式POSIX线程API调用的代码进行互操作.例如,以下代码:

int foo;
#pragma omp threadprivate(foo)

__thread int bar;

thread_local int baz;

int func(void)
{
   return foo + bar + baz;
}
Run Code Online (Sandbox Code Playgroud)

编译成:

    .globl  foo
    .section        .tbss,"awT",@nobits
    .align 4
    .type   foo, @object
    .size   foo, 4
foo:
    .zero   4
    .globl  bar
    .align 4
    .type   bar, @object
    .size   bar, 4
bar:
    .zero   4
    .globl  baz
    .align 4
    .type   baz, @object
    .size   baz, 4
baz:
    .zero   4

    movl    %fs:foo@tpoff, %edx
    movl    %fs:bar@tpoff, %eax
    addl    %eax, %edx
    movl    %fs:baz@tpoff, %eax
Run Code Online (Sandbox Code Playgroud)

这里.tbssELF部分是线程本地BSS(未初始化数据).以相同的方式创建和访问所有三个变量.

目前,与其他编译器的互操作性较少受到关注.英特尔没有实施,thread_local而Clang仍然缺少OpenMP支持.