Beefy Boxes and Bandwidth Generously Provided by pair Networks
good chemistry is complicated,
and a little bit messy -LW
 
PerlMonks  

Re^4: Trying to Understand the Discouragement of Threads

by BrowserUk (Patriarch)
on Nov 18, 2014 at 13:45 UTC ( #1107569=note: print w/replies, xml ) Need Help??


in reply to Re^3: Trying to Understand the Discouragement of Threads
in thread Trying to Understand the Discouragement of Threads

Ostensibly, starting a new interpreter in a second thread is as simple as starting one in your main thread.

And whatever you load into that interpreter will be completely separated and isolated from any other thread+interpreter you happen to have running -- so long as you stay away from process global entities, like the environment, filehandles etc. And that's where things start to get complicated.

You can even get creative and switch the interpreter contexts around, so that you effectively break the 1 thread tied to 1 interpreter link.

Running isolated interpreters is relatively trivial, the problems arise when it comes to communicating between them, and sharing memory. And that's where the iThreads coders went "wrong"(*). They opted to use a 'clone everything' implementation, which whilst meeting their brief -- to provide a fork emulation on Windows -- creates all (literally all) of the perceived problems that have been (incorrectly) labeled as the "heaviness of threads". The heaviness lies entirely with the attempt to provide Copy-On-Write semantics without the use of Copy-On-Write OS support. It means you have to copy everything up front just in-case it gets written to. (Which is all the more dumb frustrating as Windows does actually support Copy On Write memory!)

The second big problem with Ithreads, namely the size cost of shared memory, is equally frustrating, because it is equally fixable!

For example, when you create a shared array, a (bog standard) array is allocated in 'shared space'; and then each thread that accesses it gets a tied array that 'redirects' reads and write to the shared array. But the dumb bit is that the tied array in user threads consist of a tied (standard) perl array of tied scalars. Tied scalars (with their associated magic) are bigger than most ordinary scalars; which means that the 'placeholder' tied array is often much bigger than the actual shared array! Which is a nonsense.

The entire tied array could consist of a single blessed scalar containing a reference to the shared array. Full stop. When a FETCH or STORE (or any other tied array method) is invoked, the arguments provide all the required information to allow the shared array to be access and/or modified. Imagine how much lighter (and faster) shared arrays would be, if each thread only required a single shared scalar placeholder, instead of a full array of (fat) shared scalars!

Likewise for hashes; And %ENV; and for filehandles; and all other process global entities.


With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
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.
  • Comment on Re^4: Trying to Understand the Discouragement of Threads

Replies are listed 'Best First'.
Re^5: Trying to Understand the Discouragement of Threads
by Jenda (Abbot) on Nov 18, 2014 at 16:24 UTC

    If I'm not mistaken, the previous implementation of threads tried the "share all" way and failed miserably so the ithreads were developed to have something that, even if heavy, at least ... works.

    Jenda
    Enoch was right!
    Enjoy the last years of Rome.

      If I'm not mistaken, the previous implementation of threads tried the "share all" way and failed miserably so the ithreads were developed to have something that, even if heavy, at least ... works.

      I'm not advocating the "share all" way. Absolutely not. Actually what I'm advocating is share less. Indeed, only share what I ask to share; and only when I ask to share it.

      What makes iThreads heavy, is not the threads, nor the 'protected' memory of normal lexicals; it is the wholesale cloning of the process global state -- %ENV, file&dir handles, special global variables; etc. etc. -- every time you start a thread. What I'm advocating it to skip all of that entirely. It only makes sense because they are trying to emulate fork, where all that stuff has to be cloned, because you're starting a new process, so the new process cannot access the process global state of its parent. That simply isn't true of a new thread; and duplicating (for example) stdin/stdout/stderr simply makes no sense. having cloned them, they are still connected to the same place, printing from one thread still conflicts wth printing from another thread, so what did you gain by cloning them? Nada.

      And the second thing that makes threads "heavy", is the duplication of everything that has been loaded, or instantiated into the current thread before the clone. This is *never* what I want in a new thread. Again, this only makes sense in the context of fork emulation; and fork emulation only makes sense if it allows unix scripts that use the forking paradigm to run unchanged on the emulating platform -- ie. windows -- but it doesn't! Signals would have to accurately emulated; and they aren't (and probably can't be!). Non-blocking IO would have to work the same; (it could be made to, but it doesn't). The fork emulation is almost completely useless.

      But interpreter threads with complete segregated memory, works. And works well. So well in fact that the concept is being used in just about every new programming language that provide kernel threading. And, as you mentioned, and as the previous Perl threading experiment proved, given the nature of the old-fashioned programming style used by Perl's internals -- global state ($/,$\,$" etc.); static tables & god objects; non-reentrant functions et al. -- it is the only way to retro-fit threads into it.

      But completely isolated threads throws away one of their major advantages: the ability to use divide and conquer threads upon (shared) large, homogeneous datasets. So you need a way to share state; which is currently implemented by threads::shared, but in a decidedly non-optimal way.

      Now imagine that instead of the indiscriminate cloning required by the useless & unused fork emulation, you put all shared state -- all the globals and environment in addition to the shared lexicals -- into a single SHARED::* stash (similar to what is done for shared lexicals now, but with the addition of the process global entities (GSEs)), and then modify the GV* functions to lock and access that SHARED::namespace whenever a GSE is accessed.

      Bingo! Isolated, interpreter threads, clean and empty at the point of spawning, with full, controlled access to GSEs. With a sensible implementation of thread::shared::tie to avoid the gratuitous use of memory that it currently uses, and you end up with threads that start faster & lighter, and have full access to GSEs and lightweight shared memory, whilst retaining the implicitly non-shared state of normal lexicals.


      With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
      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.

        If you don't clone something, you can either disable access or share. As you can't disable access to most of that stuff, you've got to share it and once you share it, you've got to protect it and still run the risk of ending up in an inconsistent state thanks to race conditions and lack of locks.

        I do not think your blame of the fork emulation is correct.

        Jenda
        Enoch was right!
        Enjoy the last years of Rome.

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others rifling through the Monastery: (4)
As of 2022-08-12 06:00 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found

    Notices?