Ticket #2340: inlineCallbacks_benchmark.py

File inlineCallbacks_benchmark.py, 5.1 KB (added by itamarst, 15 years ago)

Benchmarks for inlineCallbacks and friends

Line 
1#!/usr/bin/env python2.5
2from twisted.internet import defer, reactor
3from twisted.python import failure
4from twisted.python.util import mergeFunctionMetadata
5
6import time, gc
7
8## defgen
9
10# BaseException is only in Py 2.5.
11try:
12    BaseException
13except NameError:
14    BaseException=Exception
15
16
17# inlineCallbacks with slightly buggy speed hack:
18def _faster_inlineCallbacks(result, g, deferred):
19    # improvement: shortcirtuit already trigered deferreds
20    while 1:
21        try:
22            # Send the last result back as the result of the yield expression.
23            if isinstance(result, failure.Failure):
24                result = g.throw(result.type, result.value, result.tb)
25            else:
26                result = g.send(result)
27        except StopIteration:
28            # fell off the end, or "return" statement
29            deferred.callback(None)
30            return deferred
31        except defer._DefGen_Return, e:
32            # returnVal call
33            deferred.callback(e.value)
34            return deferred
35        except:       
36            deferred.errback()
37            return deferred
38
39        if isinstance(result, defer.Deferred):
40            # a deferred was yielded, get the result.
41            if result.called:
42                # XXX this test is buggy
43                result = result.result
44                continue
45
46            def gotResult(r):
47                _faster_inlineCallbacks(r, g, deferred)
48
49            result.addBoth(gotResult)
50            return deferred
51
52    return deferred
53
54
55def faster_inlineCallbacks(f):
56    def unwindGenerator(*args, **kwargs):
57        return _faster_inlineCallbacks(None, f(*args, **kwargs), defer.Deferred())
58    return mergeFunctionMetadata(f, unwindGenerator)
59
60
61#### Itamar's version - untested error handling path
62
63class _Result(object):
64    def __init__(self):
65        self._def = defer.Deferred()
66        self.returnVal = self._def.callback
67       
68    def wait(self, d):
69        d.pause()
70        d.addCallbacks(self._result, self._error)
71        return d
72   
73    def _error(self, flr):
74        try:
75            d = self._gen.throw(flr.type, flr.value, flr.tb)
76            if d is not None: 
77                d.unpause()
78        except:
79            self._gen.errback(failure.Failure())
80           
81    def _result(self, result):
82        try:
83            d = self._gen.send(result)
84            if d is not None:
85                d.unpause()
86        except:
87            self._gen.errback(failure.Failure())
88
89def itamar_inlineCallbacks(f):
90    def wrapper(*args, **kwargs):
91        r = _Result()
92        r._gen = f(r, *args, **kwargs)
93        try:
94            d = r._gen.next()
95            if d is not None:
96                d.unpause()
97        except:
98            r._def.errback(failure.Failure())
99        return r._def
100    return mergeFunctionMetadata(f, wrapper)
101
102
103### Benchmarkable functions for different variations:
104
105# twisted 2.1 deferredGenerator
106@defer.deferredGenerator
107def defgen21(iters, df):
108    for i in range(iters):
109        d = df(i)
110        d = defer.waitForDeferred(d)
111        yield d
112        n = d.getResult()
113    yield n + 1
114
115# itamar's inlineCallbacks
116@itamar_inlineCallbacks
117def itamar(result, iters, df):
118    for i in range(iters):
119        n = yield result.wait(df(i))
120    yield result.returnVal(n + 1)
121
122# twisted 2.5 inlineCallbacks
123@defer.inlineCallbacks
124def inlineCallbacks25(iters, df):
125    for i in range(iters):
126        n = yield df(i)
127    defer.returnValue(n + 1)
128
129# twisted 2.5 inlineCallbacks with speed optimization
130@faster_inlineCallbacks
131def faster_inlineCallbacks25(iters, df):
132    for i in range(iters):
133        n = yield df(i)
134    defer.returnValue(n + 1)
135
136
137def imDone(res, func, then):
138    elapsed = time.clock() - then
139    print "%0.3f secs  <--  %s (result=%s)" % (elapsed, func.func_name, res[0])
140
141def err(flr):
142    print "failure: %s" % flr
143
144
145def pretriggeredDeferred(i):
146    """Deferred that is already triggered."""
147    return defer.succeed(i)
148
149def callLaterDeferred(i):
150    """Deferred that isn't immediately triggered (benchmarks slower due to callLater)."""
151    d = defer.Deferred()
152    reactor.callLater(0, d.callback, i)
153    return d
154   
155def go(numDeferreds, benchmarkedFunction, deferredFactory):
156    """Run benchmarked function 10000 times with numDeferreds created.
157
158    Uses deferredFactory to create Deferreds.
159    """
160    ir = range(1000)
161    then = time.clock()
162    defs = []
163    for i in ir:
164        defs.append(benchmarkedFunction(numDeferreds, deferredFactory))
165    result = defer.gatherResults(defs)
166    result.addCallback(imDone, benchmarkedFunction, then)
167    result.addErrback(err)
168    result.addCallback(lambda x: gc.collect())
169    return result
170
171def printer(_x, m):
172    print m
173
174
175def runall(df):
176    print "\n==== %s" % (df.__doc__.strip(),)
177    result = defer.succeed(1)
178    for i in (3, 10, 50):
179        result.addCallback(printer, "== %d Deferreds" % (i,))
180        for f in (defgen21, itamar,
181                  inlineCallbacks25, faster_inlineCallbacks25):
182            result.addCallback(lambda x, f=f: go(i, f, df))
183    return result
184
185def main():
186    runall(pretriggeredDeferred).addCallback(
187        lambda x: runall(callLaterDeferred)).addCallback(
188        lambda x: reactor.stop())
189
190reactor.callWhenRunning(main)
191reactor.run()