【问题标题】:why this variable is not optimized out为什么这个变量没有优化出来
【发布时间】:2021-10-15 16:46:50
【问题描述】:

我有一个简单的 sn-p,其中编译器优化了变量“done”

#include <pthread.h>
#include <stdbool.h>

bool done = false;

void *func(void *args)
{
    done = true;

    return NULL;
}

main()
{
    pthread_t p1;
    
    pthread_create(&p1, NULL, func, NULL);
    printf("waiting\n");
    
    while(!done)
    {}
    printf("moving on...\n");
}

这里,在没有 volatile 关键字的情况下,变量“done”被优化掉并进入无限循环。

我正在编译使用:

gcc -O2 volatile.c -lpthread

但是当我使用简单的 func() 版本时:

#include <stdbool.h>

bool done = false;

void func()
{
        done = true;

}

main()
{
        func();
        printf("waiting\n");

        while(!done)
        {}
        printf("moving on...\n");
}

但是这里的变量“done”没有优化出来,两者有什么区别?

编译器在第二种情况下猜到“完成”会改变一些方式,但在 Pthread 的情况下不会?

我的 GCC 版本是:

gcc --version gcc (Ubuntu 5.4.0-6ubuntu1~16.04.12) 5.4.0 20160609

【问题讨论】:

  • “优化”是什么意思?您没有提供您使用的任何命令行选项。
  • variable "done" is optimized away 你怎么知道它被“优化掉了”?只是true。我是bool done = true;,你是说bool done = false;吗?
  • @KamilCuk,是的,我刚刚更新了 sn-p。
  • 那么,现在,这不是一个无限循环..
  • 顺便说一下,对于可移植的 C,volatile 不足以防止数据竞争和未定义的行为。您必须将 done 设为 atomic 类型(这也将防止不必要的优化)

标签: c linux


【解决方案1】:

变量done 在这两种情况下都没有优化。优化的(在这两种情况下)是 while 循环中 done 的 read。该读取被提升出循环并在循环之前发生一次;然后循环运行 0 次或无限次,具体取决于循环前读取的 done 的值。它实际上变成了

    if (!done)
        while (true) {}
    else
        while (false) {}

然后将其进一步简化为

    if (!done) while (true) {}

【讨论】:

  • 那么当然,在第二个程序中,在循环开始之前我们肯定有done == true(因为func()已经返回),所以没有执行无限循环,一切都很好。在第二种情况下,存在数据竞争,因此done 在循环之前读取时可能为真或假,因此我们可能会得到一个无限循环或根本没有循环。 (或鼻恶魔,因为毕竟数据竞赛是 UB。)
【解决方案2】:

以下建议的代码:

  1. 包括缺少的头文件:“stdio.h”用于“printf()”和“fprintf()”
  2. 正确退出线程函数
  3. 正确地使“主”线程等待子线程 以避免“消耗所有可用的 CPU 周期”
  4. 正确检查对“pthread_create()”的调用状态——此函数未设置“errno”
  5. 正确处理“args”参数以避免编译器警告
  6. 消除“无用”变量“完成”
  7. 为 'main()' 使用有效的签名
  8. 包含适当的水平间距以提高可读性
  9. 消除不需要的标头“stdbool.h”

现在,建议的代码:

#include <pthread.h>
#include <stdio.h>  // printf(), fprintf()
#include <stdlib.h> // exit() and EXIT_FAILURE

//bool done = false;

void *func( void *args )
{
    (void)args;   // eliminate warning about unused argument
    //done = true;

    //return NULL;
    pthread_exit( NULL );
}

int main( void )   //use valid signature for 'main()'
{
    pthread_t p1;
    
    if( pthread_create( &p1, NULL, func, NULL ) != 0 )
    {
        fprintf( stderr, "call to pthread_create() failed\n" );
        exit( EXIT_FAILURE );
    }

    printf( "waiting\n" );
    pthread_join( p1, NULL );  // wait for sub thread to exit
    
    //while(!done)
    //{}
    printf( "moving on...\n" );
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多