remoteD
Document Version: 0.8, 12/22/2003
1. General Information
1.1 Introduction:
remoteD is an easy to use interface for forked multi-process programming. It provides a simple dictionary interface for accessing data shared between processes and is capable of auto starting a server process when needed.
Any python object that can be pickled can be stored in a remoteD 'share'. The name comes from the idea of a remote Dictionary object, which is the interface provided to your program. reads and writes are atomic, and explicit locking is available for situations that call for multi-command consistency.
1.2 Installation
remoteD is installed via the distutils package using
the normal python setup.py mechanism. The full instructions for
installing remoteD are:
- 1) Uncompress the remoteD distribution via tar, winzip, or the appropriate platform specific utility.
- 2) Enter the newly created remoteD directory created in step 1 via a command line.
- 3) Type python setup.py install
- 4) Enjoy.
1.3 License:
remoteD is currently distributed under a (new) BSD style license. to view the full text of this license click here.
Commercial support and customization of remoteD is available, please contact
NeuroKode Labs, LLC
for more information.
2 Objects, methods and constants
Constants:
UNIXSOCK - used to represent a UNIX socket type
IPSOCK - used to represent a IP socket type
DEFAULTSOCK - normally IPSOCK
Method/Attribute: |
Description: |
2.1 Module Level Functions |
initShare() |
Summary: initShare returns a stubShare object in the calling process, and will fork and start a local share-server if one does not already exist.
Usage: SharedD = remoteD.initShare()
Arguments:
- port: the port to connect to, or create the server on.
- sType: Socket Type (UNIXSOCK, IPSOCK, DEFAULTSOCK)
- AutoKill: whether the server should kill itself if 5 seconds pass with no children, normally 1 (yes).
|
createShareServer() |
(Internal) createShareServer() is used by initShare() to create and fork a server.
|
do_command() |
(Internal) do_command() is used by stubShare to preform commands on the share object.
|
2.2 stubShare Objects |
| stubShare objects behave like dictionary objects in most respects. |
__init__() |
Summary: creates a stubShare
Usage: myShare = stubShare(port, sType, host)
Arguments:
|
has_key() |
Just like a dictionary object, but checks for the key's existence in the remoteD share-server
|
keys() |
Just like a dictionary object, but retrieves keys from the server. You should assign the results from this and reuse when possible to keep from pounding the server - see section 3.
|
Lock() |
Summary: Locks the server associated with the share object
Usage: SharedD.Lock()
|
Unlock() |
Summary: Unlocks a locked share object
Usage: SharedD.Unlock()
|
newProc() |
Summary: stubShare.newProc() forks then performs the function passed to it.
Usage: SharedD.newProc(child_function, [arg1, arg2])
Note: The child_function must expect a stubShare object as it's first argument. This new stubShare object will be the child process's reference to the share server.
|
3 Examples & notes
# example #1
# hello world with remoteD
import remoteD, time
SharedD = remoteD.initShare()
def child_function(Shared, arg1, arg2):
Shared["myresult"] = "Hello World"
SharedD.newProc(child_function, [arg1, arg2])
while not SharedD.has_key("myresult"):
time.sleep(0.2)
print "The other process got '" + SharedD["myresult"] + "' as the answer"
Notes and considerations.
remoteD is fast - over 1.5k processes were created and connected to a single share-server on a 1GHz P3 machine using UNIXSOCK sockets, very little slowdown was observed. Slowdown occurs when physical memory is exhausted by the number of processes running.
Individual reads and writes to a remoteD share-server are atomic, so key-level locking is not explicitly provided - it exists implicitly in the way remoteD works. However, order of operations between processes is not guaranteed (just as in a multi threaded program). If you need consistent access to multiple keys, using .Lock() and .Unlock() is recommended.
Remember that objects in the share-server are remote, and attempting to create a reference to one will instead copy its value. For example:
SharedD['bar'] = 5
foo = SharedD['bar'] # foo now contains the value 5
foo += 2 # foo is now 7, SharedD['bar'] is still 5
Thus you must explicitly write changes back to the server when you are complete, in this case you'd just assign back to SharedD['bar'].
While reads and writes are fast, using them willy-nilly can cause contention at the server. Consider the following:
foo = SharedD['bar'] / SharedD['sue']
print str(SharedD['bar']) + " divided by " + str(SharedD['sue']) + " is " + foo
Each key is accessed twice on the server. Instead, you should do:
bar = SharedD['bar']
sue = SharedD['sue']
foo = bar / sue
print str(bar) + " divided by " + str(sue) + " is " + str(foo)
This is faster since local copies of the variables are used. Additionally, if another process were to change the value of 'sue' or 'bar' between the division and print line in the original example you could see an inconsistent answer printed, possibly "5 divided by 8 is 2"!
Remember that only individual operations are atomic, thus the following may not function as expected if .Lock() and .Unlock() are not used:
Shared['bar'] = Shared['bar'] + 1
Other processes could be futzing with bar between the read and the write to the share server.
Use .Lock() and .Unlock() carefully - no provision exists to auto-unlock the server is a child process halts while holding a lock. Use try clauses and always do an .Unlock() if you get an exception.