如何欺骗编译器优化内存读取?

时间:2012-08-30 18:42:06

标签: c++ compiler-construction

我听说过编译器优化。

例如:

时的那个
while(myBool)
    doStuff();

编译器知道你不在while循环中修改myBool,它只读取myBool一次并且不会每次都检查它。

这可能会受volatile关键字影响吗?

所以我试图“欺骗”编译器,它认为价值没有改变。

int main()
{
    struct st 
    {
        int a;
        int b; //I want to make difference by writing volatile here
    }s;

    s.b = 1;

    while(s.b > 0)
    {
        *((&s.a)+1) = 0;
        std::cout << "test" << std::endl;
    }
}

但即使启用了完全优化(vs2012),这也不会欺骗编译器。这可能是非常蹩脚的伎俩:)

我怎么欺骗它?它甚至可能吗?

我的目标是创建简单的单线程程序,使用volatile关键字打印“test”并打印无限次“test”而不使用关键字。

编辑:不幸的是我对汇编程序不好,所以如果内存读数首先被优化的话,我真的无法阅读:)

3 个答案:

答案 0 :(得分:2)

您正在做的是未定义的行为。您无法通过写信s.b来访问*((&s.a)+1),因为无法保证s.as.b之间没有任何填充字节。

如果你想强迫那里没有填充,请查看你的编译器选项。对于GCC / Clang,您将使用__attribute__((packed))声明结构。对于MSVC,您可以使用#pragma pack directive

没有充分的理由使用该代码访问s.bs.a。在这种情况下你真正应该做的是,假设你有充分的理由不直接访问s.b,那就是使用一个数组(volatile,如果需要的话)。保证数组在内存中是连续的,无需特殊的属性/编译指示。

此外,在某些情况下,编译器不会进行某些优化:如果i是一个变量,并且您写入a[i],然后读取a[1],则编译器可以'假设a[1]不只是写入,因此它无法将其缓存在寄存器中。如果您有两个不相关的变量ab并且您写入*(&a+1)然后从b读取,则编译器假设{ {1}}不只是写给。这是b未定义行为的一个非常好的理由,因为它导致编译器做出不真实的假设并导致程序以奇怪的方式运行。

答案 1 :(得分:1)

我认为您在询问如何确保编译器假定值几乎相同(在您的示例中为true),因此compliler会优化此值。 如果您这样做,那么您正在寻找一组特定于编译器的关键字。 这是我在g ++中使用的

#define predict_true__(exp)     __builtin_expect((exp), 1)
#define predict_true__(exp)       (exp)

这样你可以做到

while ( predict_true__( myBool ))
    doStuff() ;

myBool会有效运行。 希望它有所帮助,你会知道在使用视觉的情况下要看什么。

另外:我发现以下帖子谈到它。不过,我担心这是一个悲伤的话题:likely/unlikely equivalent for MSVC

答案 2 :(得分:0)

简短的回答是structs不保证在记忆中是连续的。数组是。你无法像这样访问结构!

可能有帮助的问题:Pointer arithmetic for structs。它详细介绍了如何做到这一点及其原因。但总结一下,结构的字段之间可能有填充,也可能没有填充。我特别喜欢查理对这个问题的回答。