簡體   English   中英

為什么gcc沒有優化全局變量?

[英]Why gcc isn't optimizing the global variable?

我試圖通過一個例子來理解C中volatile和編譯器優化的行為。

為此,我提到:

哪里用volatile?

為什么C需要揮發性?

https://software.intel.com/en-us/blogs/2007/11/30/volatile-almost-useless-for-multi-threaded-programming

以上所有帖子都至少有一個與信號處理程序相關的答案,為此,我編寫了一個簡單的代碼來實際實現並觀察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測試中穿插了對usleepprintf調用。 這些是不透明的庫調用。 編譯器無法透視它們,因此必須假設它們可以訪問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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM