I've been meaning to add a yield() function to the SDL API for a while. I've also been mulling around with different ideas to improve the spinlocks.
Here's the current spinlock (pseudo-code):
while (locked) delay(0)
My theory was that if the lock was contended, it's better to retry a few times before sleeping. I also figured using the OS's thread yield function was a better way of giving up the current timeslice than sleeping for 0 time.
Here was my new spinlock:
if (!locked) return
while (true)
for (i = 0; i < 4; ++i)
if (!locked) return
yield()
Astonishingly, my FIFO benchmark went from 3 seconds to 10 seconds!
Thinking about this a little, it makes sense... the CPU probably doesn't see the value change in as few cycles as the loop is, so all we're really doing is wasting time in the loop. When there's high contention, as in my FIFO test, this just means the whole system is working harder.
So, I went back to my basic spinlock, but I used a yield instead of delay(0):
while (locked) yield()
Oddly enough, my FIFO benchmark wasn't any faster, in fact it was 4 seconds instead of the original 3 seconds. Apparently on Mac OS X sched_yield() is a more expensive function than nanosleep().
My conclusion?
Always benchmark your changes, even if they're obviously an improvement, and don't be afraid to leave well enough alone. :)
In OS theory, there are five states that a process can exist in: new, ready, running, waiting, and terminated. Yielding moves a process from the "running" state to the "ready" state. Sleeping moves a process from the "running" state to the "waiting" state. Processes in the "ready" state are waiting to be scheduled onto a CPU. Processes in the "waiting" are waiting on some event (usually I/O) and let the scheduler know when they expect to be back in the "running" state (via the parm to the sleep function). So, don't yield when you can sleep, since yielding doesn't give the scheduler any hints as to how soon you need your process executing again!
ReplyDeleteYou are right that even if we are sure that our new solution is better then this one we should always benchmark the current solution.So that in any case we feel that this is better we don't need to rework.Many times I reworked in such situations as I never thought to come back when a new solution seems more promising
ReplyDelete