[英]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.