LLVM 编译器——这是一个优化错误吗?
LLVM compiler - Is this an optimization bug?
我有一个与 LLVM 编译器优化级别相关的有趣问题。我正在使用:
- Xcode 8.2.1
- LLVM 8.0
最好用示例代码来说明。我把问题归结为一个简单的 objective-c class。请先看下面的代码:
@interface Foo() {
BOOL is_loading;
}
@end
@implementation Foo
- (void)test {
printf("started loading \n");
// set loading flag to YES
is_loading = YES;
// schedule a timer to fire in 2 seconds, to simulate the end of loading
[NSTimer scheduledTimerWithTimeInterval:2.0
target:self
selector:@selector(timerFired)
userInfo:nil
repeats:NO];
// wait asynchronously until loading flag is set to NO
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
while (is_loading) {
// loop until timer event modifies is_loading flag
}
printf("finished loading \n");
});
}
- (void)timerFired {
printf("timer fired \n");
// set loading flag to NO
is_loading = NO;
}
@end
如果你实例化classFoo
并调用load
方法,它会模拟一个加载进度,并异步观察is_loading
标志来判断是否加载完成。
之后,控制台输出将是这样的:
started loading
timer fired
finished loading
但是如果你打开编译器优化,你会看到这个输出:
started loading
timer fired
显然 while 循环永远不会结束并且执行无法到达下一个 printf() 消息。
我是否遗漏了发生这种情况的明显原因,或者它可能是优化程序错误?
正如 Apple 在其 synchronization page 中所述,编译器在优化代码时可能不会多次加载该变量。它不知道它可能是从另一个线程编辑的,所以会发生这种情况。
将变量标记为 volatile
将强制编译器在每次需要时加载该值,而这不会发生。
即使是 Sami 的回答也是对您问题的回答,这可能会产生误导。
由于 volatile
变量 不是 线程安全的,当块形成两个不同的线程访问 is_loading
时,您的整个方法可能会失败。 volatile
的用法是 而不是 用于线程同步。
改用 GCD 信号量。
我有一个与 LLVM 编译器优化级别相关的有趣问题。我正在使用:
- Xcode 8.2.1
- LLVM 8.0
最好用示例代码来说明。我把问题归结为一个简单的 objective-c class。请先看下面的代码:
@interface Foo() {
BOOL is_loading;
}
@end
@implementation Foo
- (void)test {
printf("started loading \n");
// set loading flag to YES
is_loading = YES;
// schedule a timer to fire in 2 seconds, to simulate the end of loading
[NSTimer scheduledTimerWithTimeInterval:2.0
target:self
selector:@selector(timerFired)
userInfo:nil
repeats:NO];
// wait asynchronously until loading flag is set to NO
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
while (is_loading) {
// loop until timer event modifies is_loading flag
}
printf("finished loading \n");
});
}
- (void)timerFired {
printf("timer fired \n");
// set loading flag to NO
is_loading = NO;
}
@end
如果你实例化classFoo
并调用load
方法,它会模拟一个加载进度,并异步观察is_loading
标志来判断是否加载完成。
之后,控制台输出将是这样的:
started loading
timer fired
finished loading
但是如果你打开编译器优化,你会看到这个输出:
started loading
timer fired
显然 while 循环永远不会结束并且执行无法到达下一个 printf() 消息。
我是否遗漏了发生这种情况的明显原因,或者它可能是优化程序错误?
正如 Apple 在其 synchronization page 中所述,编译器在优化代码时可能不会多次加载该变量。它不知道它可能是从另一个线程编辑的,所以会发生这种情况。
将变量标记为 volatile
将强制编译器在每次需要时加载该值,而这不会发生。
即使是 Sami 的回答也是对您问题的回答,这可能会产生误导。
由于 volatile
变量 不是 线程安全的,当块形成两个不同的线程访问 is_loading
时,您的整个方法可能会失败。 volatile
的用法是 而不是 用于线程同步。
改用 GCD 信号量。