Beefy Boxes and Bandwidth Generously Provided by pair Networks
go ahead... be a heretic
 
PerlMonks  

Re: Why use threads over processes, or why use processes over threads?

by BrowserUk (Pope)
on Nov 11, 2003 at 13:10 UTC ( #306176=note: print w/ replies, xml ) Need Help??


in reply to Why use threads over processes, or why use processes over threads?

Threading ostensibly has two main advantages over forking.

  1. Spawning a thread is, on those OS's that support this natively, cheaper in terms of memory and CPU, than forking a process.

    This is because a thread should consist of little more than a scheduler object containing a set of registers, a stack segment and some scheduler administration state. Unlike a process, there should be no need to copy large amounts of memory, as threads should be able to re-use the existing process' copy of memory.

  2. Inter-thread communication is inherently faster and cheaper then inter-process communication.

    It's the very fact that memory is shared between the threads, that make this possible.

The problem with perl's implementation of threads is that the non-reentrancy of perl's code segments, combined with the lack of a suitable engrained model for semaphores and synchronisation. The result is the only way to approach a 'safe' and cross-platform implementation of threads in perl is to emulate forking. Performing this emulation throws away both advantages that threading had to start with.

Despite heroic efforts on behalf of the developers, the underlying, rather old-fashioned, non reentrant nature of perl's core requires that everything, code and data segments be copied. This completely wipes out both advantages. First, the copying is done in user mode rather than kernel mode, throwing away all the years of optimisations and testing that now exists in kernels that support forking natively. Having copied everything, each thread now has it's own copy of every piece of memory, throwing away the benefits of COW on those platforms that support it, and requiring hookey and clumsy a bit-at-a-time, duplication and serialisation of all shared data, effectively throwing away the second advantage of threads -- direct, fast access to shared memory.

The result is, that perl threading implementation as is, is at best utilitarian, and at worst, broken. This is unsurmountable given the nature of perl's core as is, and would only be fixable with a complete re-write of the perl core. The problem runs very deep. Even the POSIX C-runtime that underlies so much of the perl core is inherently non-reentrant, and without reentrancy built-in from the lowest levels, making effective use of threads, where these are natively fast and efficient, simply isn't possible.

Personally, I am pinning my hopes on Perl 6 (and maybe Ponie) to bring the true benefits of threading to perl, but I have my fears that the predominately unix-guru based nature of the development teams, and indeed, the whole development processes, mean that even these will likely concentrate on the strengths of unix-based models and modes of operation, and that will prevent the kernel from being tuned (or tunable) for models and modes of operation that originate outside of the unix platform.

Sadly, I don't see the cross-platform support at the deepest levels of perl development improving to the point where those of us that use perl on non-unix or unix-clone platforms will ever truly gain access to the kernel strengths of our platforms. The nature of 'other' platforms isn't that they are worse or inferior to unix, nor that they are better or superior to unix, they are simply different. They tackle the same set of problems and choose different solutions. It might be possible to come up with a perl 'model' of the OS, that is flexible enough to encompass all underlying platforms in a transparent(ish) manner if sufficient effort where expended to do so, but while the development process continues to be "Do what works on unix and then try and force fit that mechanism onto non-unix platforms.", the lot of those who choose or have to use non-unix platforms will always be one of playing catch-up on builds, reliance upon 3rd-parties, and a mismatch between what we know is possible and what we can actually do.


Examine what is said, not who speaks.
"Efficiency is intelligent laziness." -David Dunham
"Think for yourself!" - Abigail
Hooray!
Wanted!


Comment on Re: Why use threads over processes, or why use processes over threads?
Re: Why use threads over processes, or why use processes over threads?
by Abigail-II (Bishop) on Nov 11, 2003 at 13:48 UTC
    non reentrant nature of perl's core
    This non-reentrant nature of perl's core goes much deeper than many people realize. It's not that there's a bunch of function that are reentrant, and that perl now has to call the *_r equivalents. It goes deeper. Even things that on the Perl level are static are non-reentrant. Like fetching the value of a scalar. For instance, print $var can't be done in parallel, because $var might not have the pOK flag set, which means the Perl has to convert the numerical value of $var to a string value - which means that the underlaying datastructure is modified. (This is the reason why variables are not shared by default).

    Abigail

      Agreed. I recently spent a lot of time exploring ways of trying to untilise threads more effectively (under Win32), and discovered the nature and scale of the problem. It hasn't put me off of the benefits of threads over processes in my environment, nor in the wider context where they are a natural part of the system. It has, however, put me off of trying to utilise them to any great extent from perl.

      They still have their utility under Win32, as the absence of a native fork, means that there is little or nothing to choose between forking and threading, but using threads does expose a little (very little) more control of the multi-processing facilities than the fork emulation.

      I wish I could be more enthusiastic, but the deep rooted nature of perl's non-reentrancy, as you noted, make it near impossible to improve the situation, give the current implementation of perl.

      From what I have understood of the Parrot architecture, the objectification of the fundemental types, should mean that it would (will?) be possible to cheaply and transparently serialise access to individual scalars. Embedding a semaphore within every scalar, and using it for every write operation may sound horrendous, but applied at the core level, it shouldn't be that problematic or costly. However, if it is not done at the core level, trying to retroactively add it would be disasterous.

      Time will tell, but I won't be holding my breath.


      Examine what is said, not who speaks.
      "Efficiency is intelligent laziness." -David Dunham
      "Think for yourself!" - Abigail
      Hooray!
      Wanted!

Re: Re: Why use threads over processes, or why use processes over threads?
by tilly (Archbishop) on Nov 11, 2003 at 15:57 UTC
    Does it make you more comfortable with the future of threading to realize that elian was for years the threading champion for Perl 5?

      Yes. I have no doubt as to the quality or skills of any of the people involved in the perl development -- be it P5, or P6. Were it that I was half as good as most of them.

      My only fear is that a lack of non-unix platform people in that august band will result in a lack of consideration to non-unix concepts and strengths.


      Examine what is said, not who speaks.
      "Efficiency is intelligent laziness." -David Dunham
      "Think for yourself!" - Abigail
      Hooray!
      Wanted!

Re: Re: Why use threads over processes, or why use processes over threads?
by chromatic (Archbishop) on Nov 11, 2003 at 18:28 UTC
    This is because a thread should consist of little more than a scheduler object containing a set of registers, a stack segment and some scheduler administration state. Unlike a process, there should be no need to copy large amounts of memory, as threads should be able to re-use the existing process' copy of memory.

    You could change a few words and the same would apply to forking a process on Linux and, I believe, *BSD. (I also believe it fits to most modern Unix variants these days, but I've only read the source for Linux.)

      Of course, the big difference between threads and processes remains the fact that the latter need their personal page tables. There is probably quite a bit of room for optimization of this process on the MMU design level left though. (Why recreate the entire page table set? COW could be applied there as well.)

      This is not going to happen overnight, but I'm certain that at some point, the effective overhead of processes over threads will be zero. It is very close already, but not there yet.

      Makeshifts last the longest.

      I'm not familiar with linux or most other unixes, but there would have to be at least the replication of filesystem objects -- duping of open file handles, sockets etc. and associated state.

      I would imagine that it also requires the creation of handles to the existing memory objects in order to handle COW etc.

      In addition, every write, which on the evidence of Abigail above, can frequently mean a perl-level read, will result in a memory copy operation (though I'm not sure what the granularity is). There will also be some amount of overhead associated with detecting writes to shred memory segments. Whether this is a software or hardware interupt, the effect upon L1 and L2 caches etc. can be expensive too.

      It's unclear to me how forking handles other shared handles like DB connections, hardware connections to tape drives, serial ports and the like, but I think that it is probably down to the user to handle this rather than fork.

      None of these things is individually expensive, but the convenience of spawning a thread, without requiring any of this is considerable. The greatest use, and the greatest benefit from threads is for performing asynchronous reads (from whatever). This use is simply not possible with forks. The select model just doesn't compare for usability, and event-driven models require you to throw away even standard structured programming techniques, never mind object-oriented models and revert to relying upon global state.

      Finally, the benefits of co-routines are totally absent from the forking model, but are almost trivial to implement using threads.

      I don't see threads and processes as an either/or proposition. In an ideal world, the programmer would have both spanners in his toolkit, and would be free to choose whichever is appropriate for the task at hand. For some tasks one is appropriate, for others, the other. In some cases a mixture of the two makes perfect sense, if the underlying system supports both efficiently. The best choice will sometimes be dictated by the underlying system.


      Examine what is said, not who speaks.
      "Efficiency is intelligent laziness." -David Dunham
      "Think for yourself!" - Abigail
      Hooray!
      Wanted!

        The greatest use, and the greatest benefit from threads is for performing asynchronous reads (from whatever). This use is simply not possible with forks.

        I'm sure I must just be misunderstanding your point. Could you please expand on what you meant by this (with an example)?

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others having an uproarious good time at the Monastery: (7)
As of 2014-12-25 20:05 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    Is guessing a good strategy for surviving in the IT business?





    Results (163 votes), past polls