iSina has asked for the wisdom of the Perl Monks concerning the following question:

I have written an extensive simulation program in perl (protein folding to be more precise). Now I want to run it on more than one processors, but I have no idea about how a program is converted to a multithreaded one. I know that there is a thread module in perl for that but I have some basic questions.

1- Is multithreading what is commonly known as parallel algorithms, ie do I have to implement parallel algorithms to run a multithreaded program to take advantage of more than one processors.

2- If the answer to first question is no: my program is very complicated so would it take it a long time to convert it to multithread one or is it a simple task?

Replies are listed 'Best First'.
Re: Multithreading, how to?
by BrowserUk (Patriarch) on Dec 31, 2008 at 01:49 UTC

    The answer to both questions is: it depends upon the program. Some programs lend themselves to multi-threading and the conversion can be quite simple. Others, even quite simple programs, can be very hard.

    There really is no way to tell you whether it will be easy or hard without at least a fairly detailed high level description of the program. Even then, even if that high level description suggests that the problem could lend itself to threading reasonably easily, whether it does or not, will depend a lot upon how you have coded your program to solve that problem.

    Also, threading isn't the only option possible. Running multiple copies of the program on different subsets of the total dataset may be a simpler option.

    In the end, the best way to advise you would be to see the code. (Don't take that as an invitation to post thousands of lines of code here!). A high level description of the program and it dataflows would be a good starting point.


    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: Multithreading, how to?
by sflitman (Hermit) on Dec 31, 2008 at 05:45 UTC
    Take a look at CPAN module Parallel::Simple. This way you can simply pass prun a list of named arrayrefs with different arguments (data chunks as above) and let it do all the heavy lifting. You can even collect all the return values.

    From the docs:

    prun( foo => sub { print "$$ foo\n" }, bar1 => [ \&my_bar_func, @args ], bar2 => [ \&my_bar_func, @other_args ], ) or die( Parallel::Simple::errplus() );
    If the machine you're running on is multicore this ought to leverage the extra iron, I'd use the '1' keystroke of the top command to see all cores and see if they're being utilized. This assumes a kind of parallelism where there are no dependencies between data chunks, like Fold@Home or SETI@Home.

    HTH, SSF

Re: Multithreading, how to?
by gwadej (Chaplain) on Dec 31, 2008 at 04:41 UTC

    BrowserUK has some good points, but there's one other thing worth saying. Building a multi-threaded program is much harder than building a single threaded one. You can't just spread on a little multi-threading to make it go faster. I've seen many projects collapse from underestimating the work involved.

    Running multiple processes as BrowserUK suggested is normally much easer, if you can break the data into chunks.

    G. Wade
      Building a multi-threaded program is much harder than building a single threaded one. You can't just spread on a little multi-threading to make it go faster.

      Actually, often you can.

      As a simplistic example, take the problem in Averaging Elements in Array of Array, and Ikegami's solution as a starting point. If you wrap the first (nested) loops of that solution up into a subroutine that returns the array of sums, then divides them to produce the averages, you have a single threaded solution that will run in time T.

      Now assume you have a machine that has 4 cpus/cores. If you call that same subroutine, passing a subpartiton of the total dataset, 4 times--each time in a different thread:

      #! perl -slw use strict; use threads; use threads::shared; sub sumEm{ my $tid = threads->tid; my( $AoARef, $first, $n ) = @_; print "$tid: $first - ", $first + $n - 1; my @sums = ( 0 ) x 4; for my $i ( $first .. $first+$n-1 ) { $sums[ $_ ] += $AoARef->[ $i ][ $_ ] for 0 .. 3; } return @sums; } our $N ||= 1e6; our $T ||= 4; my @AoA:shared; $#AoA = $N; for( 0 .. $N -1 ) { $AoA[ $_ ] = &share([]); @{ $AoA[ $_ ] } = map rand( 100 ), 1 .. $T; } my $perThread = int $N / $T; my @threads = map{ threads->create( \&sumEm, \@AoA, $_* $perThread, $perThread ) } 0 .. $T-1; my @aves = ( 0 ) x 4; for my $thread ( @threads ) { my @subtotals = $thread->join; $aves[ $_ ] += $subtotals[ $_ ] for 0 .. 3; } $_ /= $N for @aves; print "@aves";

      The chances are that it will arrive at the same answer in something less than T/3 the time. (I don't have a quad cpu machine on which to prove this!).

      I've seen many projects collapse from underestimating the work involved.

      I'm gonna make three (totally hairy-arsed, unfounded, speculative) guesses about those projects:

      • They weren't in Perl using iThreads.

        iThreads are imperfect, badly documented, and widely misunderstood.

        But, they have some very unique properties that you will see being copied more and more as multi-core machines and threading become ubiquitous.

        Indeed, I think, Artur Bergman will, belatedly, be recognised as having contributed something quite special to the world of computing. Something so special, that--when the world catches up with him--will eventually put him up there with the likes of Lovelace, Turing, Knuth and Berners-Lee.

      • They were in some language in which OO-techniques predominated, or were unavoidable.

        It's not that OO and threads don't mix--well, they don't in Perl, but that for other reasons. It is that people will insist on trying to share objects across threads. This creates problems--which relate to the 'horizontal partitioning' and/or 'synchronisation' problems below, but compound them.

        There is no problem, barring an (artificial) Perlish limitation, with passing objects between threads. The problem arises when trying to code classes such that their instances and methods can be called concurrently on multiple threads. If you avoid that scenario, about 80% (speculative) of the problems simply disappear.

      • The architects were writing their first multithreaded project, and made the beginner's mistake of trying to:
        • Partition the problem horizontally rather than vertically.

          That is, they tried to partition the algorithm(s) across threads, rather than the data.

        • And/or: tried to synchronise the threads.

          This is the threading FAQ error number one. They attempted to share data, rather than communicate information, across threads.

      Threads needn't be hard. Certainly no harder, and often easier than forking. There are a raft of commonly cited, difficult to solve, problems that bug threading. And there has been much research, many papers, and a raft-squared proposed, usually complex, or (computationally and/or algorithmically) costly solutions to those problems.

      But the simplest solution is invariably overlooked: avoid them!

      That is, rather than trying to come up with complex and costly solutions to the well-known problems with locking, sharing and synchronisation--just don't use algorithms and techniques that require them.

      iThreads are a damn good start down that road.


      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.

        Much as I respect your opinion, you've kind of taken an odd approach on this one. Yes, I can agree that, if

        • a problem is embarrassingly parallel
        • the single-threaded solution is designed to make use of that parallelism
        • the implementation of the solution is clean enough to see that solution
        • changes for maintenance have not obscured the original implementation

        The transformation can be relatively straight-forward. Unfortunately, most of the projects I was working on are based in reality and were not ideal. It only takes violating one of the above to make the conversion quite difficult or impossible.

        Most of the mistakes I've seen with multi-threading (including my own early work) were from people convinced by a simple exercise like the one you suggest. Scaling that out to a larger production program does not necessarily work as well.

        In cases where people I've worked with have implemented multi-threaded solutions from the beginning, this wasn't as much of an issue. But, I've dealt with a lot of cases where the code had existed for some time before someone decided to just make it multi-threaded, real quick. Those usually did not go well.

        My main advice when talking to anyone about threading has always been to simplify the code as much as possible. Your comment about avoiding algorithms that require synchronization is good advice, when designing from scratch. When working on an existing program, shared state may have crept in without being noticed.

        I'm not trying to say that threads are impossible to get right, don't use them. I'm trying to say that (like security and quality) threads are not something you can just smear on some code and get benefits. Analysis and design are required to make it work.

        I will grant that I do not have as much experience with iThreads, but every solution that I have run across that makes threading really easy, has turned out to have major caveats.

        G. Wade
Re: Multithreading, how to?
by Wiggins (Hermit) on Dec 31, 2008 at 18:02 UTC
    I would not advise starting into multi-threading with a task that you really care about. There is a certain 'mind-set' that you have to be comfortable with to make most 'complex' architectures productive.

    By 'complex', I am talking about paradigms with names like 'asynchronous programming', 'multi-processing', 'multi-tasking', 'distributed processing', and 'multi-threading'. The general definition for all of these centers around partitioning a task into smaller tasks that are either independent, or parallel, or tightly constrained; and then using a technology (threads, sockets, forks & pipes, shared memory) to stitch them together into a cohesive unit.

    Compare the image of yourself fixing a complex dinner, and then imagine 5 10-year old children doing that same job, with yourself standing in the door handing out written instructions to each.

    The 'mind set' I first mentioned is the ability to do the partitioning, defining the APIs, understanding the strengths and weaknesses of your selected technology. Then add the understanding of 'critical sections', 'atomic actions', 'shared resources', 'resource locking' and deadly embraces. This part is like turning off the lights in the kitchen, and having a way for the kids to keep the others away from the hot burner or not to reach across the cutting board while someone else is dicing onions.

    'Tis better to start with filling 2 glasses with cold water; than to make a holiday meal for an entire family.

    It is always better to have seen your target for yourself, rather than depend upon someone else's description.

      And that is exactly the kind of, somebody-I-knew-tried-it-once-and-told-me-it-was-really-hard, uninformed mis-information and kneejerk FUD that pervades and predominates whenever any of the terms:

      'asynchronous programming', 'multi-processing', 'multi-tasking', 'distributed processing', and 'multi-threading'

      arise in discussion.

      Brain surgery is difficult and fraught with dangers, but sometimes it is the best--and sometimes the only--option.


      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.

        I do like the imagery though. Despite the fact that a kindergarten teacher may be able to get a group of 5 year olds to work together to accomplish some relatively impressive things, that does not mean that any random adult can do the same.

        The guy who mentored me in multi-threading suggested an analogy that I've always found useful. Writing multi-threaded code is to writing single-threaded code as playing a musical instrument is to conducting an orchestra. A single musician can be a little sloppier and ad-lib quite a bit without ruining the result. That doesn't work with an orchestra. (You could argue it does work with a good Jazz band.) This is not to say that individuals are sloppier, but a single person does not have the coordination issues that an orchestra does. (Disclaimer: I am not a musician and don't even play one on the Net.)

        The point is not that you should never use threads, but that organization and coordination become more important.

        The suggestion that you try multi-threading in a smaller experiment before applying it to a big system does not seem to be FUD to me. I would give the same advice to someone trying out a new programming language, methodology, framework, or any other piece of technology.

        I suspect that much of the FUD about threaded code out there is spread by people who jumped in with both feet, rather than doing a test program. At least, that has been my experience with some who are fanatically against any form of threading.

        G. Wade
        But don't try it witout 10+ years of education and training, at least not on your children.
        It is always better to have seen your target for yourself, rather than depend upon someone else's description.