Weird generator id() behaviour (was Re: Python code written in1998, how to improve/change it?)

Carl Cerecke cdc at maxnet.co.nz
Tue Jan 24 18:04:47 EST 2006


Fredrik Lundh wrote:
> Carl Cerecke wrote:
> 
> 
>>It turns out that generators are more efficient than the eval function
>>excuting bits of compiled code. About 20-25% faster.
> 
> 
> why are you using generators to return things from a function, when
> you can just return the things ?

Trying to find the fastest way to implement finite state machine.
The included file times 4 different ways, with functions the fastest.

The reason, I think, for the unusual sequence if id()s of the generators 
- see grandparent post - (and the reason for their poor performance 
compared to functions), is because the reference to the generator is 
being lost, then another generator is being created with the same id.
Properly done, I would expect generators to out perform functions.

These are the numbers I get on a P3 600:
$ ./foo.py
action generator overhead: 13.25
---
exec: 8.7
---
eval: 10.09
---
generators: 6.68
---
functions: 3.37


#!/usr/bin/env python

import time

s_on = compile('''
#print 'on'
action = next_action()
if action == 'lift':
     state = s_on
elif action == 'push':
     state = s_off
else:
     state = None
''','','exec')

s_off = compile('''
#print 'off'
action = next_action()
if action == 'lift':
     state = s_on
elif action == 'push':
     state = s_off
else:
     state = None
''','','exec')

def f_on():
     action = next_action()
     if action == 'lift':
         return f_on
     elif action == 'push':
         return f_off
     else:
         return None

def f_off():
     action = next_action()
     if action == 'lift':
         return f_on
     elif action == 'push':
         return f_off
     else:
         return None


def g_on():

     #print "on"
     action = next_action()
     if action == 'lift':
         yield g_on()
     elif action == 'push':
         yield g_off()
     else:
         yield None

def g_off():

     #print "off"
     action = next_action()
     if action == 'lift':
         yield g_on()
     elif action == 'push':
         yield g_off()
     else:
         yield None


def actions(n):
     import random
     for i in range(n-1):
         yield random.choice(['lift','push'])
     yield None

r = 1000000
#r = 10
next_action = actions(r).next

a = time.clock()
while next_action():
     pass
z = time.clock()
print "action generator overhead:",z-a
common = z-a

print "---"
next_action = actions(r).next
state = s_on # start state
a = time.clock()
while state:
     exec state
z = time.clock()
print "exec:",z-a-common

print "---"
next_action = actions(r).next
state = s_on # start state
a = time.clock()
while state:
     eval(state)
z = time.clock()
print "eval:",z-a-common

print "---"
next_action =  actions(r).next
s_g_on = g_on()
s_g_off = g_off()
state = s_g_on
a = time.clock()
while state:
     #print id(state)
     state = state.next()
z = time.clock()
print "generators:",z-a-common

print "---"
next_action =  actions(r).next
state = f_on
a = time.clock()
while state:
     state = state()
z = time.clock()
print "functions:",z-a-common



More information about the Python-list mailing list