On Fri, 12 Oct 2007 15:18:26 +0900, M. Edward (Ed) Borasky wrote:

> 2. There are basically two ways to do concurrency/parallelism: shared 
> memory and message passing. I'm not sure what the *real* tradeoffs are 
> -- I've pretty much had my brain hammered with "shared memory bad -- 
> message passing good", but there obviously must be *some* 
> counter-arguments, or shared memory wouldn't exist. :)

(Putting on my fake beard)  In the old days...

Shared memory is (probably) always the fastest solution; in fact, on some
OS's, local message passing is implemented as a layer on top of shared
memory.  

But, of course, if you implement concurrency in terms of shared memory, you
have to worry about lock contention, queue starvation, and all the other
things that generally get handled for you if you use a higher-level
messaging protocol.  And your software is now stuck with assuming that the
sender and receiver are on the same machine; most other messaging libraries
will work equally well on the same or different machines.

Back when machines were much slower, I had an application that already used
shared memory for caching, but was bogging down on system-level message
passing calls.  There were three worker servers that would make requests to
the cache manager to load a record from the database into the shared-memory
cache for the worker to use.   (This serialized database access and reduced
the number of open file handles.)

So I changed them to stop using the kernel-level message-queuing routines;
instead, they'd store their requests in a linked list that was kept in a
different shared memory region.  The cache manager would unlink the first
request from the list, process it, and link that same request structure
back onto the "reply" list with a return code.  The requests/replies were
very small, stayed in processor cache, etc., and there was much less
context-switching in and out of kernel mode since the queuing was now all
userland.  This also saved a lot of memory allocate/frees, another
expensive operation at the time; most message-passing involves at least one
full copy of the message.

Occasionally, the request list would empty out, in which case we had to use
the kernel to notify the cache manager to wake up and check its list, but
that was a rare occasion, and "notify events" on that system were still
much cheaper than a queue message.

I would doubt that any of this type of optimization applies to Ruby on
modern OS's, however.

-- 
Jay Levitt                |
Boston, MA                | My character doesn't like it when they
Faster: jay at jay dot fm | cry or shout or hit.
http://www.jay.fm         | - Kristoffer