From: Simon F. <sd...@uc...> - 2003-07-11 01:27:23
|
Dear SimPy Users, Stackless List, and Psyco List, (apologies for the cross-post) I'm attaching an example of a SimPy-like model that uses Stackless Python's tasklets rather than generators to simulate coroutines. I've tried to keep the syntax as SimPy-like as possible for comparison. You'll need Stackless Python (available from http://www.stackless.com). For real simulations, I'd use an AVL tree rather than bisect to keep the list of sorted timestamps, and a proper random number generator. I have a couple of problems with the code though related to Psyco: 1. The use of 'from psyco.classes import __metaclass__' results in about a two-fold speedup. Is there any way I can eke out more performance by syntactic changes? Rather strangely, I don't get this with the SimPy version (perhaps because it uses generators?). 2. The use of 'import psyco' and 'psyco.full()' results in an access violation on my Win2K machine. Can anyone else reproduce this? Best Simon #!/usr/local/bin/python2.2s # epidemic_stackless.py # Authors: Simon Frost, Klaus Muller, Tony Vignaux # Date: July 10th 2003 # A simple example of an epidemic process: # 1. Generate N susceptible individuals. # 2. For each individual, schedule a contact according to a random exponential variable. # 3. Find the individual, 'A', with the next contact; if the next contact is after # 'runTime', end # 4. Choose another individual, 'B', to contact. # 5. If individual 'B' is infected, then there is a probability 'infectProb' that A # becomes infected. # 6. If all individuals are infected, end; otherwise, go to 3. # # This uses Stackless Python (http://www.stackless.com) to simulate co-routines. # Use Psyco if available try: from psyco.classes import __metaclass__ except ImportError: pass # Import libraries import stackless import bisect import random import time class Epidemic: """A simple epidemic simulation.""" def __init__(self,infectProb,contactRate,now=0,rng=random.Random(1)): Epidemic.allIndividuals=[] Epidemic.eventList=Epidemic.EventList() Epidemic.contactRate=contactRate Epidemic.infectProb=infectProb Epidemic.now=now Epidemic.times=[now] Epidemic.numInfected=[0] Epidemic.rng=rng Epidemic.channel=stackless.channel() def activate(self): for ind in Epidemic.allIndividuals: stackless.tasklet(ind.live)() if ind.infected==0: nextEvent=Epidemic.channel.receive() Epidemic.eventList.insert(nextEvent[0],nextEvent[1]) def simulate(self,until): while Epidemic.now <= until: thisEvent=Epidemic.eventList.pop() when=thisEvent[0] who=thisEvent[1] Epidemic.now=when who.channel.send(None) try: nextEvent=Epidemic.channel.receive() except RuntimeError: nextEvent=Epidemic.eventList.pop() Epidemic.eventList.insert(nextEvent[0],nextEvent[1]) class EventList: """A class to maintain an ordered list using a bisection algorithm.""" def __init__(self): self.queue = [] def insert(self, timestamp,data): """ Insert a new element in the queue according to its priority. """ bisect.insort(self.queue, [timestamp, data]) def pop(self): """ Pop the highest-priority element of the queue. """ return self.queue.pop(0) def remove(self,timestamp,data): """ Remove item from queue. """ item_delete_point = bisect.bisect(self.queue,(timestamp,data)) del self.queue[item_delete_point-1] def is_present(self,timestamp,data): """ Determines whether item is present in queue. """ item_insert_point = bisect.bisect(self.queue, (timestamp,data)) is_present = self.queue[item_insert_point-1:item_insert_point] == [item] class Individual: """An individual in a population that is either susceptible or infected.""" def __init__(self): self.infected=0 self.channel=stackless.channel() Epidemic.allIndividuals.append(self) def contactInterval(self): return Epidemic.rng.expovariate(Epidemic.contactRate) def chooseContact(self): contact = self while contact == self: contact=Epidemic.rng.choice(Epidemic.allIndividuals) return contact def live(self): while self.infected==0: ci = self.contactInterval() Epidemic.channel.send((Epidemic.now+ci,self)) self.channel.receive() B=self.chooseContact() if B.infected ==1 and Epidemic.rng.random() <= Epidemic.infectProb: Epidemic.times.append(Epidemic.now) Epidemic.numInfected.append(Epidemic.numInfected[-1]+1) print str(Epidemic.now),Epidemic.numInfected[-1] self.infected = 1 def model(nrIndividuals,contactRate,infectProb,initialInfected,runTime): run=Epidemic(infectProb=infectProb,contactRate=contactRate) for i in range(nrIndividuals): ind=Epidemic.Individual() if i >= (nrIndividuals-initialInfected): ind.infected=1 Epidemic.numInfected[0]+=1 run.activate() run.simulate(until=runTime) if __name__=="__main__": t=time.time() run=model(nrIndividuals=100,contactRate=3,infectProb=0.003,initialInfected=1,runTime=1000) print time.time()-t |
From: Armin R. <ar...@tu...> - 2003-07-11 12:36:56
|
Hello Simon, On Thu, Jul 10, 2003 at 05:40:12PM -0700, Simon Frost wrote: > I have a couple of problems with the code though related to Psyco: The first note is that Psyco is not particularly tested with Stackless Python. Although I think it should probably work, at least to some extent. Psyco may not like Stackless moving the stack away, which I guess would result in a violation whenever you use sys._getframe(). More generally, the problem is that both Stackless and Psyco change the execution model of Python in some subtle and not a priori compatible way. I guess that I could work out a Stackless-friendly version of Psyco if there is enough demand. > 1. The use of 'from psyco.classes import __metaclass__' results in about a > two-fold speedup. Is there any way I can eke out more performance by > syntactic changes? Rather strangely, I don't get this with the SimPy > version (perhaps because it uses generators?). There is no simple answer to this. You should read in detail the Psyco manual. (Generators are indeed not supported at this time.) A bientot, Armin. |
From: Christian T. <ti...@ti...> - 2003-07-12 21:40:22
Attachments:
test_pickle.py
|
Armin Rigo wrote: > Hello Simon, > > On Thu, Jul 10, 2003 at 05:40:12PM -0700, Simon Frost wrote: > >>I have a couple of problems with the code though related to Psyco: > > > The first note is that Psyco is not particularly tested with Stackless Python. > Although I think it should probably work, at least to some extent. Psyco may > not like Stackless moving the stack away, which I guess would result in a > violation whenever you use sys._getframe(). More generally, the problem is > that both Stackless and Psyco change the execution model of Python in some > subtle and not a priori compatible way. > > I guess that I could work out a Stackless-friendly version of Psyco if there > is enough demand. Ok, one small addition from my side: Stackless 3.0 avoids the stack moving trick as much as it can. But this is not totally complete, yet. The stackless.run() code always introduces stack moves now. The only way to avoid this currently is to avoid stackless.run() at all. :-( Yes I will change that. Although you probably create all your tasklets in a way that allows soft switching, you might still create a hard switch, by running your file through execfile() or something. You can either avoid this, or you can make a new tasklet from your main code, by tasklet().become(). The attached code from test_pickle.py does this, see the line which says "temporary hack". This will vanish, pretty soon. I think, one quick way to make Stackless Psyco compatible would be to ask Psyco to not optimize hard-switched tasklets at all. I will for sure talk with Armin, how this should work in the future. Thanks for pointing this problem out. ciao - chris -- Christian Tismer :^) <mailto:ti...@ti...> Mission Impossible 5oftware : Have a break! Take a ride on Python's Johannes-Niemeyer-Weg 9a : *Starship* http://starship.python.net/ 14109 Berlin : PGP key -> http://wwwkeys.pgp.net/ work +49 30 89 09 53 34 home +49 30 802 86 56 pager +49 173 24 18 776 PGP 0x57F3BF04 9064 F4E1 D754 C2FF 1619 305B C09C 5A3B 57F3 BF04 whom do you want to sponsor today? http://www.stackless.com/ |