3

Identifying the Problem

I was busy editing a library for lua bindings to rtmidi. I wanted to fix MinGW-GCC and LLVM/Clang compilation compability. When I was done making the edits and compiling the bindings, I noticed a weird timing issue caused by std::this_thread::sleep_for() when compared to MSVC.

I understand that there are bound to be some scheduling differences between different compilers, but in the following examples you can hear large timing issues:

I have narrowed it down that this is the piece of code in question:

lua_pushliteral(L, "sleep");
lua_pushcfunction(L, [] (lua_State *L) {
    auto s = std::chrono::duration<lua_Number>(luaL_checknumber(L, 1));
    std::this_thread::sleep_for(s);
    return 0;
});
lua_rawset(L, -3);

Obviously it's about these two lines:

auto s = std::chrono::duration<lua_Number>(luaL_checknumber(L, 1));
std::this_thread::sleep_for(s);

The average waiting time that is passed to sleep_for() is around 0.01s, with some calls here and there between 0.002s - 0.005s.

Troubleshooting

First off I have checked whether the problem was present with my current version of GCC (9.2.0) by using a different version and even using LLVM/Clang. Both GCC 8.1.0 and LLVM/Clang 9.0.0 yield the same results.

At this point I can conclude there is some weird scheduling going on with the winpthreads runtime, since they depend on it and MSVC does not.

After that I tried to switch out the code with the Windows Sleep() call. I had to multiply by 1000 to adjust for the correct timing.

Sleep(luaL_checknumber(L, 1) * 1000);

As I expected, the timing issue is not present here; this tells me that winpthreads is indeed the culprit here.

Obviously I do not want to make calls to Windows Sleep() and keep using sleep_for() for the sake of portability (as in crossplatform).

The Questions

So based on what I gathered I have the following questions:

  • Is winpthread indeed the culprit? Am I perhaps missing some compiler defines that would solve the problem?
  • If winpthreads is indeed the culprit, why are the timing differences so big?
  • If there is no compiler define 'fix', what would you recommend to do tackle the problem?

To partially answer the third question (if it may come to it), I was thinking of doing something like:

#ifdef _WIN32 && MINGW
    #include <windows.h>
#endif

...

#ifdef _WIN32 && MINGW
    Sleep(luaL_checknumber(L, 1) * 1000);
#elif _WIN32 && MSVC
    auto s = std::chrono::duration<lua_Number>(luaL_checknumber(L, 1));
    std::this_thread::sleep_for(s);
#endif

Of course the problem arises that Window's Sleep() call is less precise (or so I've read).

Badope
  • 55
  • 3
  • 1
    Possible duplicate of [std::this\_thread::sleep\_for sleeps for too long](https://stackoverflow.com/questions/58436872/stdthis-threadsleep-for-sleeps-for-too-long) – ChrisMM Oct 30 '19 at 15:23
  • `std::this_thread::sleep_for` blocks for at least the time you ask for, there is no guarnatee for the maximum time it is blocked afaik – 463035818_is_not_an_ai Oct 30 '19 at 15:24
  • @ChrisMM I've read that post before and I did not find any indication about differences by compiler (or rather the difference with (not) using winpthreads). I don't think this post is a complete duplicate. – Badope Oct 30 '19 at 15:27
  • The compiler is irrelevant. It's how the hardware and OS handle threads, scheduling, etc., etc.. – ChrisMM Oct 30 '19 at 15:28
  • @ChrisMM Then why is the problem not present by using MSVC, i.e. not using winpthreads? – Badope Oct 30 '19 at 15:31
  • 1
    @Badope the behaviour you see may be different, but the guarantees you get should be the same, and they are not what you need – 463035818_is_not_an_ai Oct 30 '19 at 15:37
  • Has to do with what lower-level primitives are being used to perform the sleep, and this is done in the Standard library implementation. The library that came with Visual Studio is using calls that result in a finer-grained tick than MinGW is using, allowing for a closer approximation of the requested sleeping period. – user4581301 Oct 30 '19 at 15:37
  • Why this is is likely MinGW being based on GCC and GCC supporting a much larger umbrella of possible hardware and operating systems. GCC is more general, but when it comes to performance tuning, sometimes general just isn't good enough. – user4581301 Oct 30 '19 at 15:41
  • @Badope, I haven't looked through the entire source code of winpthreads, but it relies on calls to `Sleep` anyway. Not sure exactly how G++ implements `sleep_for` though, so it may rely on functions that don't use `Sleep` – ChrisMM Oct 30 '19 at 15:44

0 Answers0