Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"Escape" and "Clobber" equivalent in MSVC

In Chandler Carruth's CppCon 2015 talk he introduces two magical functions for defeating the optimizer without any extra performance penalties.

For reference, here are the functions (using GNU-style inline assembly):

void escape(void* p)
{
    asm volatile("" : : "g"(p) : "memory");
}

void clobber()
{
    asm volatile("" : : : "memory");
}

It works on any compiler which supports GNU-style inline assembly (GCC, Clang, Intel's compiler, possibly others). However, he mentions it doesn't work in MSVC.

Examining Google Benchmark's implementation, it seems they use a reinterpret cast to a volatile const char& and passes it to a function hidden in a different translation unit on non-gcc/clang compilers.

template <class Tp>
inline BENCHMARK_ALWAYS_INLINE void DoNotOptimize(Tp const& value) {
    internal::UseCharPointer(&reinterpret_cast<char const volatile&>(value));
}

// some other translation unit
void UseCharPointer(char const volatile*) {}

However, there are two concerns I have with this:

  1. I'm potentially incurring a function call
  2. There is the possibility a "clever" link-time optimizer might recognize UseCharPointer is small, inline it, then discard all the code I wanted kept around, or a "clever" optimizer might be allowed to perform other re-orderings I don't want it to.

Is there any lower-level equivalent in MSVC to the GNU-style assembly functions? Or is this the best it gets on MSVC?

like image 372
helloworld922 Avatar asked Nov 28 '15 19:11

helloworld922


2 Answers

While I don't know of an equivalent assembly trick for MSVC, Facebook uses the following in their Folly benchmark library:

/**
 * Call doNotOptimizeAway(var) against variables that you use for
 * benchmarking but otherwise are useless. The compiler tends to do a
 * good job at eliminating unused variables, and this function fools
 * it into thinking var is in fact needed.
 */
#ifdef _MSC_VER

#pragma optimize("", off)

template <class T>
void doNotOptimizeAway(T&& datum) {
  datum = datum;
}

#pragma optimize("", on)

#elif defined(__clang__)

template <class T>
__attribute__((__optnone__)) void doNotOptimizeAway(T&& /* datum */) {}

#else

template <class T>
void doNotOptimizeAway(T&& datum) {
  asm volatile("" : "+r" (datum));
}

#endif

Here is a link to code on GitHub.

like image 106
Tim Severeijns Avatar answered Nov 06 '22 19:11

Tim Severeijns


I was looking for a way to achieve the exact same thing in my own little benchmark lib. The frustrating thing about MSVC is that targeting x64 disallows the __asm trick while x86 allows it!

After some tries I reused google's solution without incurring additional call! The nice thing is that the solution works with both MSVC(/Ox) and GCC(-O3).

template <class T>
inline auto doNotOptimizeAway(T const& datum) {
    return reinterpret_cast<char const volatile&>(datum);
}

At the call site I simply do no use the volatile returned!

int main()
{
    int a{10};
    doNotOptimizeAway(a);
    return 0;
}

Generated ASM (Compiler Explorer)

a$ = 8
main    PROC
        mov     DWORD PTR a$[rsp], 10
        movzx   eax, BYTE PTR a$[rsp]
        xor     eax, eax
        ret     0
main    ENDP
like image 2
Xenonamous Avatar answered Nov 06 '22 19:11

Xenonamous