Beefy Boxes and Bandwidth Generously Provided by pair Networks
There's more than one way to do things

Re: Threading and join/termination question

by oiskuu (Hermit)
on Jul 31, 2014 at 21:16 UTC ( #1095820=note: print w/replies, xml ) Need Help??

in reply to Threading and join/termination question

Technically, you cannot set a thread limit with semaphores this way. It does work in practice, but there's no real guarantee that a thread, or all threads, aren't preempted right after the $sem->up().

Second point, do not detach a thread if you need to wait upon it. Grab its end result (return value) with a join(), instead.

You may not want to create too many threads (especially with fine-grained jobs). An alternative that might be suitable is to arrange for a conveyor belt with N workers. Use Thread::Queue for that. Here's a simple, if overly recursive, example:

use threads; use Thread::Queue; my $WQ = Thread::Queue->new('unit001' .. 'unit123'); sub qrun { map { $_ ? (worker($_), qrun()) : () } $WQ->dequeue_nb; } sub worker { warn "Work $_"; select(undef, undef, undef, rand 3); # sleep some return "$_!"; } print for map { $_->join } map { threads->new(\&qrun) } 1 .. 15;

Replies are listed 'Best First'.
Re^2: Threading and join/termination question
by sundialsvc4 (Abbot) on Aug 01, 2014 at 02:51 UTC

    That’s a very-terse example of a million-dollar idea.   (Great example if you’re a serious Perl-head ... not-so-easy if you’re not.)

    The idea is this:   instead of defining “a thread” as corresponding to “a unit of work to be completed,” and then trying to “limit the number of” those threads, create a pool of threads, calling each one of them “workers.”   Then, give those workers a production-line queue of things-to-do.   Each worker grabs a request off of the production-line, does it, shoves the results downstream somewhere, and then grabs another one ad infinitum, surviving until the production-line finally runs dry.   The number-of-workers (thread-pool size) should correspond to the maximum number of such units-of-work that you know this piece of hardware can predictably accomplish.   So, whether the production-line queue might be short or long, you remain confident that the work is being carried-out as fast as possible ... no matter how short or long the queue might be, the “completed units-of-work per second” will remain steady.   If you are gifted with faster hardware, you simply turn-up the number-of-workers knob a little bit more.   (And if you are gifted with a cluster of units, spread the work out among all the CPUs you have, so-many workers apiece.)

      I really have to agree with sundialsvc4. Threads are so expensive to create that you are almost aways much better off to create a set number of threads (usually no more than the physical number of cores you have, for optimal results), then using a queuing system, such as Thread::Queue, to distribute work until all the work is complete.

      Also, unless you are creating a thread that is performing a never ending task (such as listening to events from an external source), its generally not a great idea to detach them, especially if you are interested in when they complete or what they return.

      Actually I was wondering about that...are there rules of thumb for how many threads a given platform can deal with? Equal to the number of cores? Limited by memory?

        As a first pass estimate, consider your chokepoints. Is the program mostly doing disk IO, number crunching, using a lot of memory, network bandwidth, special devices, or just idle?

        If disk I/O is the bottleneck, then adding a second thread would be likely to cause the disk to thrash and slow down.

        If math is the bottleneck, then the number of cores in your box is likely to be the sweet spot. Something for each core to do, without making it context switch too often.

        Memory limitations are pretty easy. If your task takes up a lot of RAM, limit your threads to a number that won't use up all the RAM. Once your threads start having to swap to disk, performance will drop through the floor.

        If network traffic is filling up, then choose a number that is enough threads to get a good % utilization without saturating the link. (Also keep in mind that the local bandwidth on your network card is very different from the bandwidth you'll get through your final connection to the internet. Percentages will vary, and be sure to divide by 8 when you see speeds measured in megabits per second)

        If you've got a bank of special devices to operate, then you may want one for each device to keep them busy without being wasteful. Depending on the task, one thread may be enough if it can deal with each device occasionally in a loop, such as a bank of printers.

        If you are idly waiting for something, then a single other thread is handy to do a blocking wait while your main thread putters around keeping a GUI responsive.

Re^2: Threading and join/termination question
by cormanaz (Chaplain) on Aug 01, 2014 at 12:52 UTC
    I believe you are right because I ran my code and it seemed to run no faster than the unthreaded code. I will try to work out the queued version. Though subdialsvc4 is correct that it's a bit intimidating.

    As for the original question, what is in the thread object? Just the result of the sub operation, or a bunch of other data that consumes memory? Docs say a thread is a separate instance of Perl, so I'm imagining the object contains all kinds of state information.

      I suggest that “the queued version,” as presented, is “intimidating” merely because it is very terse.   (It is, so to speak, “Perl golf.”)   The essential idea is actually simple:   a worker-thread survives for a long time, retrieving units-of-work from some thread-safe queue and executing them ... of course within an eval {..} block so that the thread will survive even if the unit-of-work does not.   The number of workers, of course, determines the number of units-of-work that this system will attempt to carry out at any one time.   The number of workers (just like the number of people who are scheduled for a shift at McDonald’s ...) should be “a knob” that is easy to set.

      Perl has an interesting implementation of threading ... you need to look at the various docs (and here in PerlMonks) on that subject.

      Now, before you proceed, you should also step back and be sure that you have reasonable cause to believe that “multiple threads” will, in fact, “be likely to make this operation run faster.”   Previous entries in this thread talk a great deal about this.   One “cheap and dirty” way to explore it is to launch multiple copies of this Perl program ... as it is right now (single-threaded) ... in separate terminal-windows.   Just how many windows can you have open-and-working at the same time, and still find that each of them finishes at about the same time?   (The time commandname command in Unix/Linux can give you hard numbers.)   If you find that “two windows equals twice-as-long,” then you can immediately conclude that mutlithreading would probably be a waste of time.   On the other hand, if you find that multiple windows do let you get the work done (across all windows) in about the same amount of time, then, well-l-l-ll... guess you have two choices!   One is to dive into multithreading as described.   Another is to ... “just run it as-is in multiple terminal windows!”

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: note [id://1095820]
[choroba]: Here you're lucky if they give you 5 months

How do I use this? | Other CB clients
Other Users?
Others chilling in the Monastery: (7)
As of 2018-05-23 21:07 GMT
Find Nodes?
    Voting Booth?