【问题标题】:Compile time barriers - compiler code reordering - gcc and pthreads编译时间障碍 - 编译器代码重新排序 - gcc 和 pthreads
【发布时间】:2012-11-12 12:15:39
【问题描述】:

AFAIK 有一些 pthread 函数充当内存屏障(例如,此处为 clarifications-on-full-memory-barriers-involved-by-pthread-mutexes)。但是编译时障碍呢,即编译器(尤其是 gcc)是否意识到这一点?
换句话说 - 例如- pthread_create() 是 gcc 不执行重新排序的原因吗?

例如在代码中:

a = 1;
pthread_create(...);

确定不会重新排序吗?
来自不同函数的调用呢:

void fun(void) {
    pthread_create(...);
    ...
}

a = 1;
fun();

fun() 是否也编译时间障碍(假设 pthread_create() 是)?
不同翻译单元中的函数呢?

请注意,我对一般 gcc 和 pthreads 行为规范感兴趣,不一定是特定于 x86 的(关注各种不同的嵌入式平台)。
我也对其他编译器/线程库的行为不感兴趣。

【问题讨论】:

    标签: gcc pthreads memory-barriers


    【解决方案1】:

    因为诸如pthread_create() 之类的函数是外部函数,编译器必须确保在调用函数之前必须完成对外部函数可见的任何副作用(例如对全局变量的写入)。编译无法对a 的写入重新排序,直到在第一种情况下的函数调用之后)假设a 是全局的或可能在外部可访问)。

    这是任何 C 编译器所必需的行为,实际上与线程无关。

    但是,如果变量 a 是一个局部变量,编译器可能会在函数调用之后重新排序它(a 可能根本不会在内存中结束),除非类似a 的地址被获取并以某种方式在外部可用(例如将其作为线程参数传递)。

    例如:

    int a;
    
    void foo(void)
    {
        a = 1;
        pthread_create(...);    // the compiler can't reorder the write to `a` past 
                                //  the call to `pthread_create()`
    
        // ...
    }
    
    
    void bar(void)
    {
        int b;
        b = 1;
        pthread_create(...);    // `b` can be initialized after calling `pthread_create()`
                                // `b` might not ever even exist except as a something
                                //  passed on the stack or in a register to `printf()`
    
        printf( "%d\n", b);
    }
    

    我不确定是否有文档更详细地概述了这一点——这在很大程度上被 C 的“好像”规则所涵盖。在 5.1.2.3/3“程序执行”中的 C99 中。 C 由具有序列点的抽象机器指定,其中副作用必须完整,并且程序必须遵循该抽象机器模型,除非编译器可以推断出不需要副作用。

    在我上面的foo() 示例中,编译器通常无法推断pthread_create() 不需要设置a = 1;,因此将a 设置为值1 的副作用必须在致电pthread_create() 之前完成。请注意,如果有执行全局优化的编译器可以推断出 a 未在其他地方使用,它们可能会延迟或忽略分配。但是,在这种情况下,没有其他任何东西使用副作用,所以不会有问题。

    【讨论】:

    • 变量是否具有内部或外部链接(静态与外部)是否重要?
    • 在具有内部链接的主要变量中,可以与本地变量类似地对待。但是,如果它们甚至可以间接地被外部访问,那么编译器将不得不像对待全局变量一样对待它们。例如,如果你的线程函数使用了static 变量,那么它就可以在外部间接访问,因为线程函数是从“外部”调用的(编译器可以检测到这一点,因为指向线程函数的指针被传递给外部函数)。
    • 我真的不明白你最后的评论。恕我直言,静态变量可以由线程通过其他外部函数间接使用(在创建线程之前获取指向静态 a 的指针)。但我正在寻找更多的是正式的描述(例如,链接到 gcc 文档、邮件列表帖子)到可以重新排序和何时不能重新排序的规则。
    • @Andy:在回答您的上述评论时添加了一些内容。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-12-30
    • 1970-01-01
    • 2011-01-26
    • 1970-01-01
    • 1970-01-01
    • 2012-01-28
    • 2020-06-03
    相关资源
    最近更新 更多