Showing posts with label stackless python. Show all posts
Showing posts with label stackless python. Show all posts

Saturday, October 18, 2008

Example of Microsimulation with Stackless Python


Stackless Python is roughly Python with support for microthreads (called tasklets). Tasklets communicate with each other by sending messages through channels , i.e. similar to Erlang, but of course with Python syntax (guess which I prefer :-).  Stackless has a sibling called Greenlet Python , which is a library that can be used with traditional Python to get decent mictrothread support.

Scale
Tasklets have low overhead (compared to traditional threads) so you can have hundreds of thousands of them simultaneously, and when it gets overly crowded and busy on one machine, you can easily scale up by pickling some tasklets and send them to another machine (better known as mobile agents in computer science). Since tasklets also live Python's GIL regime, it might be idea to use the processing API to spawn several processes to better utilize multicore machines (and have many tasklets within each process), or use some of these libraries .

API Alignment Challenge
In Python 2.6 the process and thread APIs are aligned, but a missing piece would be to align those apis with stackless/tasklet apis. From my perspective a natural order would be process > thread > tasklet, and perhaps adding your favorite piece of the cloud would make sense for the API as well?

Example - simulation with 100k robots
The robots are playing a variant of musical chairs on a rectangular arena, it differs from regular musical chairs since if a chair has been sat on once, it can't be sat on again. Finding out who won is left as an exercise (hint: it can be found more than 1 place)

from random import randint
import stackless
import sys
import time

class Arena:
    def __init__(self, xdim, ydim):
        self.arena = [[None]*ydim for x in range(xdim)]
        (self.xdim, self.ydim) = (xdim, ydim)

    def find_unused_place_for_robot(self, robotname):
        (x, y) = randint(0,self.xdim-1), randint(0,self.ydim-1)
        if not self.arena[x][y]:
            self.arena[x][y] = robotname
        return self.arena[x][y] == robotname

class Robot:
    def __init__(self, name=0, arena=None, maxrounds=0):
        self.name = name
        self.arena = arena
        self.points = 0
        self.maxrounds = maxrounds

        # bind Robot's live method to it's tasklet
        self.tasklet = stackless.tasklet(self.live)()

    def live(self):
        rounds = 0
        while rounds < self.maxrounds:
            self.play()
            rounds += 1
        self.tasklet.kill()

    def play(self):
        # entire play method is atomically executed
        atomic = self.tasklet.set_atomic(True)
        if self.arena.find_unused_place_for_robot(self.name):
            self.points += 1
        self.tasklet.set_atomic(atomic)

class RobotArenaSimulator:
    def __init__(self, num_robots, xdim, ydim, maxrounds):
        self.maxrounds = maxrounds
        self.arena = Arena(xdim, ydim)
        self.robots = [Robot(id, self.arena, maxrounds)
                       for id in range(1,num_robots+1)]
    def run_preemptive(self, nopreempt_steps=1):
        tstart = time.clock()
        while stackless.getruncount() != 1:
            # run() takes out the tasklet after nopreempt_steps
            t = stackless.run(nopreempt_steps)
            # so need to add the tasklet to scheduler again
            if t: t.insert()
        return (time.clock()-tstart)

if __name__ == "__main__":
    tstart = time.clock()
    simulator = RobotArenaSimulator(num_robots = 100000,
                                    xdim = 10000, ydim = 10000,
                                    maxrounds = 10)
    simulationtime = simulator.run_preemptive()
    print "simulation time was %.2f seconds" % (simulationtime)
    print "total running time was %.2f seconds" % (time.clock()-tstart)

Tuesday, February 19, 2008

Greenlet Python is concurrently alive and kicking


As I mentioned before I am a big fan of the Python programming language, and for good reasons, in particular support for thousands of simultaneous lightweight threads (tasklets) with Stackless Python (which requires a modified Python interpreter).

Greenlet and Eventlet
What I recently discovered was Greenlets. It is a spinn-off library from Stackless Python but as opposed to Stackless it is supported by the standard Python interpreter. There are also some interesting additional libraries based on Greenlets, e.g. the Eventlet networking library.

(Hm, maybe using Greenlets with Parallel Python could be a thought)

Concurrency seems to be getting increasingly more attention, and it is great to see that Python is not falling behind, actually far from it. Maybe Python can be used to solve some of the challenges in concurrency.

Monday, January 14, 2008

Stackless Python

Erlang is currently getting a lot of attention as a language that supports concurrent programming with a large number of simultaneous lightweight processes (or threads if you prefer). But there are few reasons its ~sibling - Stackless Python - should get less attention, some preliminary benchmarks support that.

During grad school some students and I used Stackless Python in combination with MPI to create a simple simulation of a large number of players and NPCs in multiplayer games on a cluster. The game world was divided onto machines in the cluster and on each machine a few thousand tasklets (also called microthreads) each representing individual players and NPCs ran. When they ran into each other they communicated using channels. When players or NPCs came to the boundary of the game world we sent the serialized state of the tasklet representing the player or NPC using MPI messages to the neighbor CPU world.

Stackless Python turned out to be a very nice language to code in, and in fact the students and I tried to start a company providing microsimulation consulting services using it. Unfortunately such services turned out to be of much less demand than anticipated so we had to stop our efforts in that direction. But that wasn't Stackless' fault, in fact I would recommend having a look at Stackless Python if you consider doing microsimulation.

Friday, January 4, 2008

Dining Philosophers and Quarreling Kids

Concurrency in general and in particular making concurrent processes smoothly share resources are hard programming problems. The classic problem illustrating concurrency is the dining philosophers problem where the shared resources are forks. The problems the philosophers can get include resource starvation, deadlock and livelock. (Note: dining philosophers has a slight resemblence of the game of musical chairs)

The quarreling kids problem
A much less classic but way more realistic concurrency problem is the quarreling kids problem, i.e. when providing N resources (e.g. food or toys) to M kids (who might be either spoiled, tired or hungry) several situations can occur:
  1. N < M
    • Typical result: Quarreling kids since some kids doesn't get any resources
  2. N ≥ M and N mod M != 0
    • Typical result: Quarreling kids since resources are unevenly distributed
  3. N &ge M and N mod M == 0
    • Can go smoothly, but can lead to quarreling kids if:
      • the resources differ, e.g. in type, size, shape or color
      • the order/speed resources are distributed in
      • the resources doesn't match the recipient's expectations (stereotypical: the girl doesn't get the pink colored and the boy doesn't get the blue colored)
Solutions to the quarreling kids problem?
An obvious solution approach could be to let the kids select resources themselves, but that will typically make the kids that selects first happy and the rest less happy and start quarreling, and if the kids select resources at the same time that also typically leads to quarreling during selection process.
A second approach is to provide non-discrete resources that are of infinite divisible nature, but that has unfortunately practical lower boundaries.
A third approach is to provide personalized (heterogeneous) resources, so every kids gets what they anticipate, but this requires both detailed knowledge of each kids preference and is impractical due to either limited resource budget, limited resource availability or possibly other reasons (e.g. nutrition?).

So what might work?

A) Provide homogeneous resources (where N ≥ M and N mod M == 0) could work, this way the kids are likely to feel that they are fairly treated.

But what if there are fewer resources than kids (N < M)

B) Provide resources that supports and encourages communication and interaction, i.e. where sharing pays off (e.g. in a more or less continuous sport like soccer or a turn-based board game)

This sounds awfully politically correct, what is the point?

The point is that solving the quarreling kids problem most likely requires solutions that:
  1. Avoids resource sharing by having dedicated resources to each kid (e.g. a pair of skates each), like in A above, or
  2. Provides resources that are being competed for under a set of communicated and agreed-upon cooperative rules as in soccer, or waited for under as set of cooperative rules in a turn-based board game, like in B above. (Without cooperation quarreling about resources will occur)
From a programming perspective, the quarreling kids (processes) problem with dedicated resources don't have any sharing problems, and can entirely avoid complex synchronization mechanisms (e.g. locks and mutexes), communication between the kids (processes) are done using speech (messages). This approach is similar to message-based parallelism such as using (asyncronous) messages in MPI and Erlang or channels in Stackless Python.


But what is the computational analogy for solution 2?