Commit 3c453c72 authored by Jason Madden's avatar Jason Madden

Get the spawn benchmark working again [skip ci]

Current results:

| Benchmark             | gevent-libev-cext | gevent-libev-cffi             | gevent-libuv-cffi               |
|-----------------------|-------------------|-------------------------------|---------------------------------|
| gevent spawn          | 12.5 us           | 13.7 us: 1.10x slower (+10%)  | 14.2 us: 1.14x slower (+14%)    |
| gevent sleep          | 2.09 us           | 3.16 us: 1.51x slower (+51%)  | 74.4 us: 35.64x slower (+3464%) |
| geventpool sleep      | 3.46 us           | 6.20 us: 1.79x slower (+79%)  | 151 us: 43.58x slower (+4258%)  |
| geventraw spawn       | 5.34 us           | 6.54 us: 1.22x slower (+22%)  | 6.38 us: 1.19x slower (+19%)    |
| geventraw sleep       | 949 ns            | 1.66 us: 1.75x slower (+75%)  | 43.9 us: 46.25x slower (+4525%) |
| geventpool join       | 1.57 us           | 3.43 us: 2.18x slower (+118%) | 88.2 us: 56.07x slower (+5507%) |
| gevent spawn kwarg    | 14.2 us           | 12.4 us: 1.15x faster (-13%)  | 14.8 us: 1.04x slower (+4%)     |
| geventraw spawn kwarg | 7.68 us           | 8.38 us: 1.09x slower (+9%)   | 8.47 us: 1.10x slower (+10%)    |
| none spawn kwarg      | 771 ns            | 738 ns: 1.04x faster (-4%)    | 734 ns: 1.05x faster (-5%)      |

Not significant (3): geventpool spawn; none spawn; geventpool spawn kwarg

Refs #1493
parent aae6b469
"""Benchmarking spawn() performance.
""" """
from __future__ import print_function, absolute_import, division Benchmarking spawn() performance.
"""
from __future__ import print_function
from __future__ import absolute_import
from __future__ import division
import perf from pyperf import perf_counter
from pyperf import Runner
try: try:
xrange xrange
...@@ -14,7 +18,7 @@ N = 10000 ...@@ -14,7 +18,7 @@ N = 10000
counter = 0 counter = 0
def incr(sleep, **_kwargs): def incr(**_kwargs):
global counter global counter
counter += 1 counter += 1
...@@ -48,35 +52,38 @@ class Times(object): ...@@ -48,35 +52,38 @@ class Times(object):
def _test(spawn, sleep, options): def _test(spawn, sleep, options):
global counter global counter
counter = 0 counter = 0
before_spawn = perf.perf_counter() before_spawn = perf_counter()
for _ in xrange(N): for _ in xrange(N):
spawn(incr, sleep, **options.kwargs) spawn(incr, **options.kwargs)
spawn_duration = perf_counter() - before_spawn
before_sleep = perf.perf_counter()
if options.sleep: if options.sleep:
assert counter == 0, counter assert counter == 0, counter
before_sleep = perf_counter()
sleep(0) sleep(0)
after_sleep = perf.perf_counter() sleep_duration = perf_counter() - before_sleep
assert counter == N, (counter, N) assert counter == N, (counter, N)
else: else:
after_sleep = before_sleep sleep_duration = -1
if options.join: if options.join:
before_join = perf.perf_counter() before_join = perf_counter()
options.join() options.join()
after_join = perf.perf_counter() join_duration = perf_counter() - before_join
join_duration = after_join - before_join
else: else:
join_duration = -1 join_duration = -1
return Times(before_sleep - before_spawn, return Times(spawn_duration,
after_sleep - before_sleep, sleep_duration,
join_duration) join_duration)
def test(spawn, sleep, options): def test(spawn, sleep, options):
all_times = [_test(spawn, sleep, options) all_times = [
for _ in xrange(options.loops)] _test(spawn, sleep, options)
for _ in xrange(options.loops)
]
spawn_duration = sum(x.spawn_duration for x in all_times) spawn_duration = sum(x.spawn_duration for x in all_times)
sleep_duration = sum(x.sleep_duration for x in all_times) sleep_duration = sum(x.sleep_duration for x in all_times)
...@@ -86,17 +93,20 @@ def test(spawn, sleep, options): ...@@ -86,17 +93,20 @@ def test(spawn, sleep, options):
return Times(spawn_duration, sleep_duration, join_duration) return Times(spawn_duration, sleep_duration, join_duration)
def bench_none(options): def bench_none(options):
options.sleep = False
def spawn(f, sleep, **kwargs):
return f(sleep, **kwargs)
from time import sleep from time import sleep
options.sleep = False
def spawn(f, **kwargs):
return f(**kwargs)
return test(spawn, return test(spawn,
sleep, sleep,
options) options)
def bench_gevent(options): def bench_gevent(options):
from gevent import spawn, sleep from gevent import spawn, sleep, get_hub
return test(spawn, sleep, options) return test(spawn, sleep, options)
...@@ -115,13 +125,17 @@ def bench_geventpool(options): ...@@ -115,13 +125,17 @@ def bench_geventpool(options):
return times return times
try:
def bench_eventlet(options): __import__('eventlet')
from eventlet import spawn, sleep except ImportError:
from eventlet.hubs import use_hub pass
if options.eventlet_hub is not None: else:
use_hub(options.eventlet_hub) def bench_eventlet(options):
return test(spawn, sleep, options) from eventlet import spawn, sleep
if options.eventlet_hub is not None:
from eventlet.hubs import use_hub
use_hub(options.eventlet_hub)
return test(spawn, sleep, options)
def all(): def all():
...@@ -132,11 +146,27 @@ def all(): ...@@ -132,11 +146,27 @@ def all():
def main(): def main(argv=None):
import os
import sys
if argv is None:
argv = sys.argv[1:]
env_options = [
'--inherit-environ',
','.join([k for k in os.environ
if k.startswith(('GEVENT',
'PYTHON',
'ZS', # experimental zodbshootout config
'RS', # relstorage config
'COVERAGE'))])]
# This is a default, so put it early
argv[0:0] = env_options
def worker_cmd(cmd, args): def worker_cmd(cmd, args):
cmd.extend(args.benchmark) cmd.extend(args.benchmark)
runner = perf.Runner(add_cmdline_args=worker_cmd) runner = Runner(add_cmdline_args=worker_cmd)
runner.argparser.add_argument('benchmark', runner.argparser.add_argument('benchmark',
nargs='*', nargs='*',
default='all', default='all',
...@@ -157,7 +187,7 @@ def main(): ...@@ -157,7 +187,7 @@ def main():
times = func(options) times = func(options)
return times.join_duration return times.join_duration
args = runner.parse_args() args = runner.parse_args(argv)
if 'all' in args.benchmark or args.benchmark == 'all': if 'all' in args.benchmark or args.benchmark == 'all':
args.benchmark = ['all'] args.benchmark = ['all']
...@@ -171,28 +201,28 @@ def main(): ...@@ -171,28 +201,28 @@ def main():
runner.bench_time_func(name + ' spawn', runner.bench_time_func(name + ' spawn',
spawn_time, spawn_time,
globals()['bench_' + name], globals()['bench_' + name],
Options(False, False), Options(sleep=False, join=False),
inner_loops=N) inner_loops=N)
if name != 'none': if name != 'none':
runner.bench_time_func(name + ' sleep', runner.bench_time_func(name + ' sleep',
sleep_time, sleep_time,
globals()['bench_' + name], globals()['bench_' + name],
Options(True, False), Options(sleep=True, join=False),
inner_loops=N) inner_loops=N)
if 'geventpool' in names: if 'geventpool' in names:
runner.bench_time_func('geventpool join', runner.bench_time_func('geventpool join',
join_time, join_time,
bench_geventpool, bench_geventpool,
Options(True, True), Options(sleep=True, join=True),
inner_loops=N) inner_loops=N)
for name in names: for name in names:
runner.bench_time_func(name + ' spawn kwarg', runner.bench_time_func(name + ' spawn kwarg',
spawn_time, spawn_time,
globals()['bench_' + name], globals()['bench_' + name],
Options(False, False, foo=1, bar='hello'), Options(sleep=False, join=False, foo=1, bar='hello'),
inner_loops=N) inner_loops=N)
if __name__ == '__main__': if __name__ == '__main__':
......
...@@ -599,7 +599,12 @@ class AbstractLoop(object): ...@@ -599,7 +599,12 @@ class AbstractLoop(object):
self.update_now() self.update_now()
def __repr__(self): def __repr__(self):
return '<%s at 0x%x %s>' % (self.__class__.__name__, id(self), self._format()) return '<%s.%s at 0x%x %s>' % (
self.__class__.__module__,
self.__class__.__name__,
id(self),
self._format()
)
@property @property
def default(self): def default(self):
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment