Beefy Boxes and Bandwidth Generously Provided by pair Networks
Pathologically Eclectic Rubbish Lister
 
PerlMonks  

Re^4: Perl threads: using yield() and Time::HiRes usleep() in Linux/*nix

by ait (Hermit)
on May 11, 2009 at 04:09 UTC ( [id://763183]=note: print w/replies, xml ) Need Help??


in reply to Re^3: Perl threads: using yield() and Time::HiRes usleep() in Linux/*nix
in thread Perl threads: using yield() and Time::HiRes usleep() in Linux/*nix

That is correct, sir. It is logic that sleeping (using the OS), should allow for CPU to carry out more work on other threads. The question was whether or not the yield() before or after helps in any way and how yield is implemented in *nix systems with a typical vanilla Perl binary distro.

To add a bit more context: The program is a scanning and OMR (Optical Mark Recognition aka Bubble Test) corrector. So while the scanner is fetching pages and transmitting images over the USB wire, the OMR lib is processing and decoding. The scanning thread waits for an image queue to be emptied by the OMR thread before fetching for the new image. Threading alone, reduced total processing time to about one third (3 seconds down to a bit over 1 - on average).

Initially, I just added the millisecond delay so I don't hog the CPU looping on the $image_queue->pending call while the OMR thread clears the queue. I could have used 2 queues and fully sync both threads with a $queue->dequeue on the scanning thread, but the program as whole is really complex so I refrain from adding new things unless absolutely necessary.

What I was doubtful about, was if adding a yield() before or after the the delay would aid the OS somehow in yielding more CPU time to other threads of the same parent PID. Carrying out different tests it really makes no difference on my single CPU laptop (though the delay alone does help!). I will carry more test on dual-core CPUs and see if it makes any difference. But if not, what is the yield() call there for? It just calls a single no-op? What's the use of single no-op? Does it do anything more interesting on other OSs/Configurations? Can it be compiled to do anything more interesting than a single no-op?
  • Comment on Re^4: Perl threads: using yield() and Time::HiRes usleep() in Linux/*nix

Replies are listed 'Best First'.
Re^5: Perl threads: using yield() and Time::HiRes usleep() in Linux/*nix
by BrowserUk (Patriarch) on May 11, 2009 at 13:17 UTC
    But if not, what is the yield() call there for? It just calls a single no-op? What's the use of single no-op?

    On Windows, yield is implemented as sleep( 0 ), which causes the current thread to give up its current scheduler quantum, meaning that it will not get another timeslice until all other threads of equal or higher priority--that are in a 'ready to run' state--will get a timeslice before it gets another.

    On many other platforms, yield() is implemented in terms of sched_yield() which makes similar promises.

    YMMV with your platform.

    However, with both mechanisms, if no other thread is currently ready to run, then it can lead to the yielding thread getting another timeslice almost immediately. Where upon it may again check its control condition and immediately call yield again. If all other threads in the system are blocked, this can lead to what amounts to a rather expensive tight loop.

    By using a short sleep in place of a yield, it will reduce cpu burn in these nothing to do periods, as the scheduler will effectively loop internally rather than re-scheduling the yielding thread over and over.

    But using yield, either alone, or inconjunction with a short sleep can be beneficial. When you sleep for a non-zero period, there is a certain amount of work involved in setting up, checking and clean-up of the timer itself. That means that with sleep( 0.00001 ) it will take a little longer than with yeild(), before another ready thread actually gets control. This difference will likely be swamped in Perl (or other interpreted language), but can be critical in real-time systems and device drivers written in a compiled language. It's also the case that on some very old OSs, including early *nix kernels I believe, that sleep was implemented as a counting loop--effectively polling until the timer ran out--which made yield far cheaper in resource terms.

    As an aside, I'd caution you to think carefully how you arrived at your: "I want: to yield at least 250 milliseconds of time to other threads before checking for the queue again." figure in the OP. With the non-determanism of threading, such fixed precise quanta are impossible to achieve precisely, and usually misguided. When you first check the queue, you may have missed the availability of data by just one clock cycle--theoretically--and by going into a 0.25 second sleep, you are guarenteeing that you will not check again for at least 0.25 seconds. But the actual delay can, and will usually be, much longer. Once you've relinguished the cpu, your process will not again be eligable for dispatch for 0.25 seconds + the cumulative time it takes for all the other equal or higher priority threads in the system to use their timeslices.

    As a rule of thumb, it is better to use a sleep of 1/2 (or less) of the minimum time period that you believe it will take for your control condition to be met. In this way, you are most likely to get at least one check within each minimum time period. And if your chosen sleep value is greater than the scheduler's quanta, you will see no greater decrease in cpu usage by using a longer sleep period. Therefore, using the shorter sleep uses minimal extra resource, but can greatly improve responsiveness.


    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
Re^5: Perl threads: using yield() and Time::HiRes usleep() in Linux/*nix
by ikegami (Patriarch) on May 11, 2009 at 04:20 UTC

    What I was doubtful about, was if adding a yield() before or after the the delay would aid the OS somehow in yielding more CPU time to other threads of the same parent PID

    Sleeping already totally frees the CPU. I don't see how you could free it more than totally.

    But if not, what is the yield() call there for?

    When you have a CPU intensive task and you wish to give a chance to someone else to execute? It could improve the responsiveness of UI threads?

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://763183]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others examining the Monastery: (7)
As of 2024-04-24 00:54 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found