[erlang-questions] how are erlang process lightweight?

Richard A. O'Keefe <>
Wed Oct 4 02:18:51 CEST 2006


Ben Dougall <> is still confused.

And THIS is confusing him:
	This was quite interesting from "Erlang Style Concurrency": "[Erlang 
	processes are] very cheap to start up and destroy and are very fast to 
	switch between because under the hood they're simply functions."
	
Erlang processes are *NOT* functions.

Erlang process : Erlang function
    ::
Posix or Windows thread : C function

The principles are exactly the same.
However,

    (1) Posix and Windows thread implementations are SEVERELY limited by
        the fact that they are designed to support statically typed
        languages with no garbage collector information which can create
	pointers to objects in the stacks and save those pointers anywhere.

	That means that Posix and Windows thread implementations
	CANNOT MOVE OR RESIZE STACKS, because they don't know where the
	pointers are and could not fix them.  (Note that static typing
	isn't the main problem; the Burroughs B6700 operating system
	could and did dynamically move and resize stacks with no worries
	at all, because the hardware required pointers to be tagged, so
	the operating system could be absolutely sure of finding and
	fixing all of them.  I was stunned back in 1979 to discover how
	primitive other computers were.)

	Because Posix and Windows thread implementations cannot move or
	resize stacks, 
	(A) you the programmer have to say how big they will be
	(B) to get this right it is absolutely essential that the C
	    compiler tell you how big stack frames are (but it never does)
	    and how deep the stack can get (but it never does that either).
	    So you have to over-estimate with a rather large safety factor.
	(C) heaven help you if you guess wrong, because the hardware,
	    operating system, and run-time library will cheerfully see
	    your program crash and burn.
	    So you REALLY have to over-estimate with a very large safety
	    factor.

	Erlang does keep around enough information (for garbage collection)
	so that it CAN find and fix all the necessary pointers whenever a
	stack has to be moved or resized.  So
	(D) you never have to make guesses, and
	(E) stacks can start very small => lightweight in memory use.

    (2) Posix and Windows thread implementations need the help of the
	operating system for several key operations.  Context switches
	are known to be very very bad for caches; a paper from DEC WRL
	showed that in that system a context switch could foul up your
	cache to the point where it took many thousands of instructions
	to recover, and of course by that time you have another context
	switch.

	The Erlang process implementation is user-level, sometimes
	called "green threads".  No kernel memory is needed for an
	Erlang process.  Erlang process switching does mean that the
	stack data for the process switched out of is gradually lost
	from the cache, BUT what goes into the instruction cache is the
	emulator, and that stays cached.  There are no traps out to the
	operating system and back again (which can be quite slow; deep
	recursion on SPARCs used to be very painful because of that).
	So Erlang process switching is cheap => lightweight in time.

    (3) You might imagine that Java solves problems (1) and (2), and
	indeed it does.  But Java is an imperative programming language;
	computation proceeds by smashing the state of mutable objects.
	If two or more processes try to access the same mutable object
	they can get terribly confused.  So every access to an object
	that *might* be shared with another process has to be 'synchronised'
	(or is it 'synchronized'? I wish they had chosen a word that
	everyone agreed how to spell).  This is called "locking".  Much of
	the locking is done implicitly when you call synchronised methods,
	but that doesn't affect the point that there has to be a heck of a
	lot of it.

	Java implementors have put unbelievable amounts of work into trying
	to make locking fast.  They still can't make it as fast as not locking.
	Although Java had Vector, it acquired ArrayList.  What's the
	difference?  Vector expects to be shared, and does oodles of locking;
	ArrayList expects not to be shared, and does no locking.  It's faster.
	Although Java had StringBuffer, it acquired StringBuilder?  What's
	the difference?  Just that StringBuffer expects to be shared, and
	does oodles of locking; StringBuilder has exactly the same interface
	but expects not to be shared, and does no locking.  It's faster.

	In Erlang, there are no mutable data structures[*], and in any case,
	no process can access another process's data in any way, so there is
	no need for locking in routine calculations.  Obviously, loading and
	unloading modules requires locking, and data base access had better
	involve some kind of locking, but routine calculations marching over
	data structures and building results DON'T need any locking.
	[*] I lied:  there is something called "the process dictionary".
	But that is not and cannot be shared with any other process, so you
	STILL don't need locking.

	Result: Erlang needs little or no locking for routine calculations
	=> lightweight in time.

	Even message sending and receiving could, I believe, be done
	without locking using lock-free queues.  I have no idea whether it
	would be worth while.




More information about the erlang-questions mailing list