2

I've looked around a fair amount and can't seem to find what I'm looking for, but let me first stress that I'm not looking for a high-precision sleep function.

Here's the background for the problem I'm trying to solve:

I've made a memory mapping library that operates a lot like a named pipe. You can put bytes into it, get bytes out of it, and query how many bytes are available to read/write, all that good stuff.

It's fast (mostly) processes communicating using it will average at 4GB/s if they're passing chunks of bytes 8KBs or larger. Performance goes down to around 300MB/s as you approach 512B chunk size.

The problem:

Very occasionally, on heavily loaded servers, very large lag times will occur (Upwards of 5s). My running theory for the cause of this issue is that when large transfers are taking place (larger than the size of the mapped memory), the process that's writing data will tight poll to wait for more space to be available in the circular buffer that's implemented on top of the memory map. There are no calls to sleep, so the polling process could be hogging the CPU for no good reason! The issue is that even the smallest call to sleep (1ms) would absolutely demolish performance. The memmap size is 16KB, so if it slept for 1ms every 16KB, performance would drop to a best-case scenario of 16MB/s.

The solution:

I want a function that I can call that will relinquish the CPU, but makes no limitations on when it gets rescheduled by the operating system (Windows 7 in this case).

Has anyone got any bright alternatives?/Does anyone know if such a function exists?

Thanks.

jknielse
  • 701
  • 1
  • 6
  • 10

5 Answers5

3

According to the MSDN documentation, on XP or newer, when you call Sleep with a timeout of 0 will yield to other processes of equal priority.

A value of zero causes the thread to relinquish the remainder of its time slice to any other thread of equal priority that is ready to run. If there are no other threads of equal priority ready to run, the function returns immediately, and the thread continues execution.

http://msdn.microsoft.com/en-us/library/windows/desktop/ms686298(v=vs.85).aspx

Another option that will require more work but that will work more reliably would be to share an event handle between the producer and consumer process. You can use CreateEvent to create your event and DuplicateHandle to get it into your other process. As the producer fills the buffer, it will call ResetEvent on the event handle and call WaitForSingleObject with it. When the consumer has removed some data from the full shared buffer, it will call SetEvent, which will wake the producer which was waiting in WaitForSingleObject.

mkimball
  • 764
  • 4
  • 9
  • Thanks dude, this is exactly what I'm looking for. I actually have a version of the library that uses CreateEvent, OpenEvent, ResetEvent, et al, in regression testing at the moment, but I just wanted to explore this sleep(0) thing as a last resort in case the problem persists. – jknielse Aug 21 '13 at 01:24
  • 1
    @jknielse: Actually, `Sleep(0)` relinquishes its time slice unconditionally, but that's probably *not* what you're looking for. See below. – user541686 Aug 21 '13 at 01:26
  • 1
    Events are the way to go. Let the scheduler wake your process when there is something to do instead of burning CPU time. – brian beuning Aug 21 '13 at 01:33
  • Awesome. Thanks for the killer responses everyone. – jknielse Aug 21 '13 at 01:42
  • Keep in mind that "relinquishing the thread" isn't that relevant if both threads are running in parallel on two cores. – MSalters Aug 21 '13 at 09:18
1

std::this_thread::yield() probably does what you want. I believe it just calls Sleep with 0 in most implementations.

Billy ONeal
  • 104,103
  • 58
  • 317
  • 552
1

You need the SwitchToThread() function (which will only relinquish its time slice if something else can run), not Sleep(0) (which would relinquish its time slice even if nothing else can run).

If you're writing code that's designed to take advantage of hyperthreading, YieldProcessor might do something for you too, but I doubt that'll be helpful.

user541686
  • 205,094
  • 128
  • 528
  • 886
  • The two communicating processes *are* different processes as opposed to threads. Unless I misunderstand, I believe I *need* to relinquish my timeslice in order to let the other processes run. – jknielse Aug 21 '13 at 01:32
  • @jknielse: My understanding (I could be wrong though) that `SwitchToThread` only cares about threads, not the particular processes they belong to. If you see evidence to the contrary then let me know, but as far as I know, Windows considers a "process" to be merely the *context* in which a thread runs -- it doesn't "schedule processes"; it schedules threads, and `SwitchToThread` doesn't seem to mention anything to the contrary. – user541686 Aug 21 '13 at 01:51
  • THe documentation explicitly contradicts your first statement: `SwitchToThread` *will* yield execution for the remainder of the time slice if something else can run. (Did you really mean _even_ if ? That is the opposite of _if and only if_) – MSalters Aug 21 '13 at 09:12
1

You're incorrectly assuming a binary choice. You now are always busy-waiting because sleep always would be a bad idea.

The better solution is to try a few times without sleeping. If that still fails (because the map is full, and the other thread isn't running), then you can issue a true sleep. This will be sufficiently rare that on average you'll be sleeping microseconds. You could even check the realtime clock (RDTSC) to determine how long you've spent busy-waiting before surrendering your timeslice.

MSalters
  • 173,980
  • 10
  • 155
  • 350
0

If you're operating under .Net, you can look into the Thread::Yield() method.

It may or may not help with your specific scenario but it's the correct way notify the scheduler that you want to relinquish the remainder of your timeslice.

If you're running in a pre-.Net environment (seems unlikely if you're on Windows 7), you can look into the Win32 SwitchToThread() function instead.

paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953