Search code examples
c++volatile

How to trick compiler to optimize memory reading?


I heard about compiler optimizations.

For example the one when:

while(myBool)
    doStuff();

compiler knows you do not modify myBool inside a while loop, it just read myBool once and doesn't check it every time.

This can be affected by volatile keyword right?

So I have tried to "trick" compiler, it thinks value was not changed.

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;
    }
}

But even with full optimizations turned on (vs2012), this does not trick the compiler. This is probably very lame trick :)

How can I trick it? Is it even possible?

My goal is to create simple single-threaded program, which prints "test" with volatile keyword used and prints infinite-times "test" without the keyword.

EDIT: Unfortunately I am not good with assembler, so I really can not read if the memory reading was optimized out in the first place :)


Solution

  • What you're doing is undefined behavior. You can't access s.b by writing to *((&s.a)+1) instead since there's no guarantee that s.a and s.b don't have any padding bytes between them.

    If you want to force there to be no padding, look into your compiler options. For GCC/Clang, you'd declare the structure with __attribute__((packed)). For MSVC, you'd use the #pragma pack directive.

    There's no good reason to be accessing s.b through s.a using that code. What you should really do in this case, assuming you have a good reason for not accessing s.b directly, is use an array (volatile, if need be). Arrays are guaranteed to be contiguous in memory without needing special attributes/pragmas.

    Furthermore, the compiler won't make certain optimizations in some cases: if i is a variable and you write to a[i] followed by a read of a[1], the compiler can't assume that a[1] wasn't just written to, so it won't be able to cache it in a register. Whereas if you have two unrelated variables a and b and you write to *(&a+1) and then read from b, the compiler will assume that b was not just written to. That's one very good reason why *(&a+1) is undefined behavior, since it leads the compiler to make untrue assumptions and cause your program to behave in strange ways.