2010-10-16 37 views
5

编辑和后约翰内斯宝贵的回答,改进我的问题这里是不稳定的吗?

bool b = true; 
volatile bool vb = true;  
void f1() { } 
void f2() { b = false; } 

void(* volatile pf)() = &f1; //a volatile pointer to function 

int main() 
{ 
    //different threads start here, some of which may change pf 
    while(b && vb) 
    { 
     pf(); 
    } 
} 

所以,让我们忘了同步一会儿。问题是b是否必须被宣布为不稳定。我已阅读标准和排序 - 知道易失性语义的正式定义(我甚至几乎理解它们,这个词几乎就是关键)。但是,让我们在这里有点不正式。如果编译器在循环中看到b没有办法改变,那么除非b是易失性的,它可以优化它,并假定它等于while(vb)。问题是,在这种情况下,pf本身是不稳定的,那么编译器是否允许假设b在循环中不会改变,即使b不是易失性的?

请不要评论和解答这段代码的风格,这不是一个现实世界的例子,这是一个实验性的理论问题。 评论和回答,除了回答我的问题,还解决更详细的挥发性的语义,你认为我误解了非常受欢迎。

我希望我的问题很清楚。 TIA

编辑一次:
这个怎么样?

bool b = true; 
volatile bool vb = true; 
void f1() {} 
void f2() {b = false;} 
void (*pf)() = &f1; 

#include <iosrteam> 
int main() 
{ 
    //threads here 

    while(b && vb) 
    { 
     int x; 
     std::cin >> x; 
     if(x == 0) 
     pf = &f1; 
     else 
     pf = &f2;  
     pf(); 
    } 
} 

这两个程序之间有一个主要的区别。如果是,有什么区别?

+0

我不知道C很好。如果此代码也是有效的C(除了bool,我认为它不存在于C中),请告诉我,以便我将C标记添加到问题中 – 2010-10-16 17:37:15

+0

它是有效的C如果您#include ybungalobill 2010-10-16 17:58:25

+0

请先写代码提出你的问题,然后问。不断改变自己的答案是没有意义的。此外,允许更改哪些线程(假设正确同步)?某些部分是否存在互斥体? (即围绕“assign-pf + call”部分?) – 2010-10-16 18:24:00

回答

3

问题是,在这种情况下,pf本身就是易失性的,那么编译器是否允许假设b在循环中不会改变,即使b不是易失性的?

这不可能,因为你说pf可能被其他线程改变,而这种改变间接如果bpf由while循环中调用即可。因此,虽然理论上不需要正常读取b,但实际上必须读取它以确定其是否应该短路(当b变成false时,它不能再读取vb)。


回答第二个部分

在这种情况下pf不挥发了,所以编译器可以摆脱它,看到f1有一个空的身体和f2b为false。它可以优化main如下

int main() 
{ 
    // threads here (which you say can only change "vb") 

    while(vb) 
    { 
     int x; 
     std::cin >> x; 
     if(x != 0) 
     break;  
    } 
} 

答到旧版本的编译器被允许优化环路走

一个条件是,循环就不会访问或修改任何挥发性对象(参见n3126中的[stmt.iter] p5)。你这样做,所以它不能优化循环。在C++ 03中,编译器不允许对该循环的非易失性版本进行优化(但编译器无论如何都是这样做的)。

请注意,能够优化它的另一个条件是该循环不包含同步或原子操作。无论如何,在多线程程序中,这应该是存在的。所以,即使你摆脱了volatile,如果你的程序编码正确,我不认为编译器可以完全优化它。如果你认为你可能会从能不能做到优化受益

+0

@Johannes:好吧,它不能优化循环,但它是否允许优化b而不读取它的值作为循环条件?即将其转换为无限循环?为了避免说无限循环,让我们假设它是(b && some_other_volatile_bool)。编译器是否允许将其转换为while(some_other_volatile_bool)? – 2010-10-16 17:43:17

+0

@Armen当然,如果'b'永远不会改变,那么C++ 03完全允许这样做。它必须是易变的,因为它不能优化它。 – 2010-10-16 17:44:22

+0

@Johannes:编辑我的问题。我也不能说我对答案完全满意。你看,你的答案也意味着如果一个循环包含一个通过指向函数的调用,而这个指针是根据用户输入在循环内部设置的,那么编译器可以假设b不会改变,我不想相信这是真的。我是不是该? – 2010-10-16 17:48:59

0

挥发性只伤害了你,或者如果它传达的东西是不正确的。

就你而言,你说这些变量可以被其他线程改变。阅读代码时,这是我看到不稳定时的假设,所以从维护者的角度来看,这很好 - 它给了我额外的信息(这是真的)。

我不知道是否优化是值得尝试的,因为你说这是不是真正的代码打捞,但如果他们不那么有没有任何理由不使用易挥发。

时,你应该在不正确的行为的结果,因为在优化正在改变代码的含义不使用易挥发。

我担心编码标准和编译器行为的细节,因为这样的事情可能会改变,即使它们不改变,代码也会改变(这可能会影响编译器) - 所以,除非你正在寻找对于这个特定代码的微观优化改进,我只是让它变得不稳定。

2

在这样的情况下,在当前C++标准中关于volatile的确切要求,据我所知,并不完全由标准定义,因为标准并没有真正处理多线程。它基本上是一个编译器提示。相反,我将解决典型编译器中发生的情况。

首先,假设编译器正在独立编译你的函数,然后将它们连接在一起。在任何一个例子中,你都有一个循环来检查一个变量,并调用一个函数指针。在该函数的上下文中,编译器不知道该函数指针后面的函数会做什么,因此在调用它之后必须始终从内存中重新加载b。因此,volatile在那里是不相关的。

扩展,为你的第一个实际案例,并允许编译器使整个程序优化,因为pf是挥发性的编译器仍然不知道它会在被指向(它甚至不能承担它要么f1f2!),因此也不能对函数指针调用中未修改的内容做任何假设 - 所以volatileb上仍然不相关。

你的第二个案例实际上是简单 - vb它是一个红色的鲱鱼。如果消除这种情况,即使在完全单线程语义中,也可以看到函数指针调用可能会修改b。你没有做任何未定义的行为,所以程序必须正确运行,而不需要volatile - 记住,如果你不考虑外部线程调整的情况,volatile是没有操作的。因此,如果图片中没有vb,则不可能需要volatile,而且很明显,添加vb不会发生任何变化。

因此,在总结:你不会在任何情况下需要volatile。不同之处在于,在第一种情况下,如果fp不是易失性的,那么足够先进的编译器可能会优化b,而在第二种情况下甚至不能在程序中的任何地方不发生变化。在实践中,我不希望任何编译器会真正地进行优化。

+0

在我的第二个例子中,编译器通常会理解pf是f1还是f2,分析这两个函数并且看到b可以改变,或者仅仅调用一个指向函数的指针就足以让它避免从全局变量中优化读取? – 2010-10-16 20:12:40

+0

而且,顺便说一句,C++ 0x确实处理多线程......很多 – 2010-10-16 20:14:05

+0

正确;我正在考虑当前的C++标准(并进行了编辑以清楚地说明)。无论如何,如果在第二个例子中编译器对函数指针可以指向什么进行分析,我会感到惊讶;我不会期望这将是一个有效的分析(尽管它在这个小例子中显然是有用的)。 – 2010-10-17 07:06:58