[英]Why gcc isn't optimizing the global variable?
我试图通过一个例子来理解C中volatile
和编译器优化的行为。
为此,我提到:
以上所有帖子都至少有一个与信号处理程序相关的答案,为此,我编写了一个简单的代码来实际实现并观察Linux中的行为只是为了理解。
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <pthread.h>
int counter = 0;
void *thread0_func(void *arg)
{
printf("Thread 0\n");
while(1)
{
}
return NULL;
}
void *thread1_func(void *arg)
{
printf("Thread 1\n");
while(counter == 0)
{
printf("Counter: %d\n", counter);
usleep(90000);
}
return NULL;
}
void action_handler(int sig_no)
{
printf("SigINT Generated: %d\n",counter);
counter += 1;
}
int main(int argc, char **argv)
{
pthread_t thread_id[2];
struct sigaction sa;
sa.sa_handler = action_handler;
if(sigaction(SIGINT, &sa, NULL))
perror("Cannot Install Sig handler");
if(pthread_create(&thread_id[0], NULL, thread0_func, NULL))
{
perror("Error Creating Thread 0");
}
if(pthread_create(&thread_id[1], NULL, thread1_func, NULL))
{
perror("Error Creating Thread 0");
}
else
{
}
while(1)
{
if(counter >= 5)
{
printf("Value of Counter is more than five\n");
}
usleep(90000);
}
return (0);
}
此代码仅供学习和理解。
我尝试使用以下代码编译代码:
gcc -O3 main.c -o main -pthread
但是编译器没有对全局变量counter
,也没有对其进行优化。
我期待*thread1_func
在永久循环中执行而if (counter >= 5)
永远不会为真。
我在这里错过了什么?
GCC版本: gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.4)
你对counter
值的if测试中穿插了对usleep
和printf
调用。 这些是不透明的库调用。 编译器无法透视它们,因此必须假设它们可以访问counter
外部变量,因此必须在这些调用之后重新加载counter
变量。
如果您将这些调用移出,代码将按预期进行优化:
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <pthread.h>
int counter = 0;
void *thread0_func(void *arg)
{
printf("Thread 0\n");
while(1)
{
}
return NULL;
}
void *thread1_func(void *arg)
{
printf("Thread 1\n");
unsigned i=0;
while(counter == 0)
{
i++;
}
printf("Thread 1: %d, i=%u\n", counter, i);
return NULL;
}
void action_handler(int sig_no)
{
printf("SigINT Generated: %d\n",counter);
counter += 1;
}
int main(int argc, char **argv)
{
pthread_t thread_id[2];
struct sigaction sa;
sa.sa_handler = action_handler;
if(sigaction(SIGINT, &sa, NULL))
perror("Cannot Install Sig handler");
if(pthread_create(&thread_id[0], NULL, thread0_func, NULL))
{
perror("Error Creating Thread 0");
}
if(pthread_create(&thread_id[1], NULL, thread1_func, NULL))
{
perror("Error Creating Thread 0");
}
else
{
}
while(1)
{
if(counter >= 5)
{
printf("Value of Counter is more than five\n");
}
usleep(90000);
}
return (0);
}
即使你使计数器变量为static
,编译器仍然不会优化,因为虽然外部库肯定不会看到计数器变量,但外部调用理论上可能有一个互斥锁,这将允许另一个线程更改变量而不数据竞赛。 现在,无论是usleep
还是printf
都没有围绕互斥锁的包装,但编译器不知道,也不进行线程间优化,所以它必须保守并在调用后重新加载计数器变量,重载是阻止您期望的优化。
当然,一个简单的解释是,你的程序是不确定的,如果信号处理程序执行,因为你应该所做的counter
volatile sig_atomic_t
,你应该已经有同步您的线程间访问它与任何_Atomic
或互斥-在一个未定义的程序中,一切皆有可能。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.