Gcc 优化条件
Gcc optimizes condition
我有以下代码:
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
char condA = 0;
volatile int vA = 0;
volatile int vB = 0;
void* thread_a(void* arg) {
while (1) {
if (1 == condA) {
vA += 1;
usleep(100);
} else {
;
// vB += 1;
// usleep(100);
}
}
return NULL;
}
void* thread_b(void* arg) {
condA = 1;
return NULL;
}
int main(void) {
pthread_t threadA;
pthread_t threadB;
if (0 != pthread_create(&threadA, NULL, thread_a, NULL)) {
return -2;
}
sleep(1);
if (0 != pthread_create(&threadB, NULL, thread_b, NULL)) {
return -1;
}
pthread_join(threadB, NULL);
sleep(1);
pthread_cancel(threadA);
printf("value A is %d\n", vA);
printf("value B is %d\n", vB);
return 0;
}
我 运行 我的 gcc 5.4.0 带有 -O0 和 -O1。
在 O0 上,输出为:值 A 为 501
在O1上,输出为:值A为0
我知道将 condA
设置为 volatile 可以解决这个问题,但为什么会这样?明明是在另一个线程设置的条件,为什么编译器还要优化?
此外,如果我取消注释 else 语句中的代码,它会按预期工作。
我找到的部分答案:https://gcc.gnu.org/onlinedocs/gcc-5.4.0/gcc/Volatiles.html#Volatiles "C has the concept of volatile objects. These are normally accessed by pointers and used for accessing hardware or inter-thread communication.[...]"
但这并不能解释一个事实,即如果我取消注释 else 语句中的代码,程序将使用 -O1。
可变的变量不在您的代码中
char condA = 0;
所以线程永远不会从内存中读取它来检查是否满足条件
.L3:
cmp al, 1
jne .L3
如果您添加 volatile
关键字,代码会更改为正确的代码
.L8:
movzx eax, BYTE PTR condA[rip]
cmp al, 1
jne .L8
gcc 处理 volatile
正确。但是 volatile 不保证任何其他东西,尤其是没有原子性和一致性。
编辑
第二个问题2的答案很简单。
编译器看不到在程序执行时可以更改此标志的 ant 方式(因为没有直接调用线程函数)。线程中也不调用任何函数(因为它们可能容易产生副作用)。因此编译器优化了不需要的存储读取并将值保留在寄存器中。
我有以下代码:
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
char condA = 0;
volatile int vA = 0;
volatile int vB = 0;
void* thread_a(void* arg) {
while (1) {
if (1 == condA) {
vA += 1;
usleep(100);
} else {
;
// vB += 1;
// usleep(100);
}
}
return NULL;
}
void* thread_b(void* arg) {
condA = 1;
return NULL;
}
int main(void) {
pthread_t threadA;
pthread_t threadB;
if (0 != pthread_create(&threadA, NULL, thread_a, NULL)) {
return -2;
}
sleep(1);
if (0 != pthread_create(&threadB, NULL, thread_b, NULL)) {
return -1;
}
pthread_join(threadB, NULL);
sleep(1);
pthread_cancel(threadA);
printf("value A is %d\n", vA);
printf("value B is %d\n", vB);
return 0;
}
我 运行 我的 gcc 5.4.0 带有 -O0 和 -O1。
在 O0 上,输出为:值 A 为 501
在O1上,输出为:值A为0
我知道将 condA
设置为 volatile 可以解决这个问题,但为什么会这样?明明是在另一个线程设置的条件,为什么编译器还要优化?
此外,如果我取消注释 else 语句中的代码,它会按预期工作。
我找到的部分答案:https://gcc.gnu.org/onlinedocs/gcc-5.4.0/gcc/Volatiles.html#Volatiles "C has the concept of volatile objects. These are normally accessed by pointers and used for accessing hardware or inter-thread communication.[...]"
但这并不能解释一个事实,即如果我取消注释 else 语句中的代码,程序将使用 -O1。
可变的变量不在您的代码中
char condA = 0;
所以线程永远不会从内存中读取它来检查是否满足条件
.L3:
cmp al, 1
jne .L3
如果您添加 volatile
关键字,代码会更改为正确的代码
.L8:
movzx eax, BYTE PTR condA[rip]
cmp al, 1
jne .L8
gcc 处理 volatile
正确。但是 volatile 不保证任何其他东西,尤其是没有原子性和一致性。
编辑
第二个问题2的答案很简单。
编译器看不到在程序执行时可以更改此标志的 ant 方式(因为没有直接调用线程函数)。线程中也不调用任何函数(因为它们可能容易产生副作用)。因此编译器优化了不需要的存储读取并将值保留在寄存器中。