diff --git a/src/greentest/bench_spawn.py b/src/greentest/bench_spawn.py
index a0c918090c26ae7b66ebdd8ae64cdb800911ae48..9cfa05b688374b93086b7962c5de9256a285d586 100644
--- a/src/greentest/bench_spawn.py
+++ b/src/greentest/bench_spawn.py
@@ -1,23 +1,16 @@
 """Benchmarking spawn() performance.
 """
 from __future__ import print_function, absolute_import, division
-import sys
-import os
-import time
+
+import perf
 
 try:
     xrange
 except NameError:
     xrange = range
 
-if hasattr(time, "perf_counter"):
-    curtime = time.perf_counter  # 3.3
-elif sys.platform.startswith('win'):
-    curtime = time.clock
-else:
-    curtime = time.time
 
-N = 100000
+N = 1000
 counter = 0
 
 
@@ -30,133 +23,178 @@ def incr(sleep, **_kwargs):
 def noop(_p):
     pass
 
+class Options(object):
+
+    # TODO: Add back an argument for that
+    eventlet_hub = None
+
+    loops = None
+
+    def __init__(self, sleep, join, **kwargs):
+        self.kwargs = kwargs
+        self.sleep = sleep
+        self.join = join
 
-def _report(name, delta):
-    print('%8s: %3.2f microseconds per greenlet' % (name, delta * 1000000.0 / N))
+class Times(object):
 
-def test(spawn, sleep, kwargs):
-    start = curtime()
+    def __init__(self,
+                 spawn_duration,
+                 sleep_duration=-1,
+                 join_duration=-1):
+        self.spawn_duration = spawn_duration
+        self.sleep_duration = sleep_duration
+        self.join_duration = join_duration
+
+
+def _test(spawn, sleep, options):
+    global counter
+    counter = 0
+    before_spawn = perf.perf_counter()
     for _ in xrange(N):
-        spawn(incr, sleep, **kwargs)
-    _report('spawning', curtime() - start)
-    assert counter == 0, counter
-    start = curtime()
-    sleep(0)
-    _report('sleep(0)', curtime() - start)
-    assert counter == N, (counter, N)
+        spawn(incr, sleep, **options.kwargs)
+
+    before_sleep = perf.perf_counter()
+    if options.sleep:
+        assert counter == 0, counter
+        sleep(0)
+        after_sleep = perf.perf_counter()
+        assert counter == N, (counter, N)
+    else:
+        after_sleep = before_sleep
+
+
+    if options.join:
+        before_join = perf.perf_counter()
+        options.join()
+        after_join = perf.perf_counter()
+        join_duration = after_join - before_join
+    else:
+        join_duration = -1
+
+    return Times(before_sleep - before_spawn,
+                 after_sleep - before_sleep,
+                 join_duration)
 
+def test(spawn, sleep, options):
+    all_times = [_test(spawn, sleep, options)
+                 for _ in xrange(options.loops)]
+
+    spawn_duration = sum(x.spawn_duration for x in all_times)
+    sleep_duration = sum(x.sleep_duration for x in all_times)
+    join_duration = sum(x.sleep_duration for x in all_times
+                        if x != -1)
+
+    return Times(spawn_duration, sleep_duration, join_duration)
 
 def bench_none(options):
-    kwargs = options.kwargs
-    start = curtime()
-    for _ in xrange(N):
-        incr(noop, **kwargs)
-    assert counter == N, (counter, N)
-    _report('noop', curtime() - start)
+    options.sleep = False
+    def spawn(f, sleep, **kwargs):
+        return f(sleep, **kwargs)
+    from time import sleep
+    return test(spawn,
+                sleep,
+                options)
 
 
 def bench_gevent(options):
-    import gevent
-    print('using gevent from %s' % gevent.__file__)
     from gevent import spawn, sleep
-    test(spawn, sleep, options.kwargs)
+    return test(spawn, sleep, options)
 
 
 def bench_geventraw(options):
-    import gevent
-    print('using gevent from %s' % gevent.__file__)
     from gevent import sleep, spawn_raw
-    test(spawn_raw, sleep, options.kwargs)
+    return test(spawn_raw, sleep, options)
 
 
 def bench_geventpool(options):
-    import gevent
-    print('using gevent from %s' % gevent.__file__)
     from gevent import sleep
     from gevent.pool import Pool
     p = Pool()
-    test(p.spawn, sleep, options.kwargs)
-    start = curtime()
-
-    p.join()
-
-    _report('joining', curtime() - start)
+    if options.join:
+        options.join = p.join
+    times = test(p.spawn, sleep, options)
+    return times
 
 
 
 def bench_eventlet(options):
-    try:
-        import eventlet
-    except ImportError:
-        if options.ignore_import_errors:
-            return
-        raise
-    print('using eventlet from %s' % eventlet.__file__)
     from eventlet import spawn, sleep
     from eventlet.hubs import use_hub
     if options.eventlet_hub is not None:
         use_hub(options.eventlet_hub)
-    test(spawn, sleep, options.kwargs)
-
-
-
-def bench_all():
-    from time import sleep
-    error = 0
-    names = sorted(all())
-
-    for func in names:
-        cmd = '%s %s %s --ignore-import-errors' % (sys.executable, __file__, func)
-        print(cmd)
-        sys.stdout.flush()
-        sleep(0.01)
-        if os.system(cmd):
-            error = 1
-            print('%s failed' % cmd)
-        print('')
-    for func in names:
-        cmd = '%s %s --with-kwargs %s --ignore-import-errors' % (sys.executable, __file__, func)
-        print(cmd)
-        sys.stdout.flush()
-        if os.system(cmd):
-            error = 1
-            print('%s failed' % cmd)
-        print('')
-    if error:
-        sys.exit(1)
+    return test(spawn, sleep, options)
 
 
 def all():
     result = [x for x in globals() if x.startswith('bench_') and x != 'bench_all']
-    try:
-        result.sort(key=lambda x: globals()[x].func_code.co_firstlineno)
-    except AttributeError:
-        result.sort(key=lambda x: globals()[x].__code__.co_firstlineno)
+    result.sort()
     result = [x.replace('bench_', '') for x in result]
     return result
 
 
-def all_functions():
-    return [globals()['bench_%s' % x] for x in all()]
-
 
 def main():
-    import argparse
-    parser = argparse.ArgumentParser()
-    parser.add_argument('--with-kwargs', default=False, action='store_true')
-    parser.add_argument('--eventlet-hub')
-    parser.add_argument('--ignore-import-errors', action='store_true')
-    parser.add_argument('benchmark', choices=all() + ['all'])
-    options = parser.parse_args()
-    if options.with_kwargs:
-        options.kwargs = {'foo': 1, 'bar': 'hello'}
-    else:
-        options.kwargs = {}
-    if options.benchmark == 'all':
-        bench_all()
+    def worker_cmd(cmd, args):
+        cmd.extend(args.benchmark)
+
+    runner = perf.Runner(add_cmdline_args=worker_cmd)
+    runner.argparser.add_argument('benchmark',
+                                  nargs='*',
+                                  default='all',
+                                  choices=all() + ['all'])
+
+    def spawn_time(loops, func, options):
+        options.loops = loops
+        times = func(options)
+        return times.spawn_duration
+
+    def sleep_time(loops, func, options):
+        options.loops = loops
+        times = func(options)
+        return times.sleep_duration
+
+    def join_time(loops, func, options):
+        options.loops = loops
+        times = func(options)
+        return times.join_duration
+
+    args = runner.parse_args()
+
+    if 'all' in args.benchmark or args.benchmark == 'all':
+        args.benchmark = ['all']
+        names = all()
     else:
-        function = globals()['bench_' + options.benchmark]
-        function(options)
+        names = args.benchmark
+
+    names = sorted(set(names))
+
+    for name in names:
+        runner.bench_time_func(name + ' spawn',
+                               spawn_time,
+                               globals()['bench_' + name],
+                               Options(False, False),
+                               inner_loops=N)
+
+        if name != 'none':
+            runner.bench_time_func(name + ' sleep',
+                                   sleep_time,
+                                   globals()['bench_' + name],
+                                   Options(True, False),
+                                   inner_loops=N)
+
+    if 'geventpool' in names:
+        runner.bench_time_func('geventpool join',
+                               join_time,
+                               bench_geventpool,
+                               Options(True, True),
+                               inner_loops=N)
+
+    for name in names:
+        runner.bench_time_func(name + ' spawn kwarg',
+                               spawn_time,
+                               globals()['bench_' + name],
+                               Options(False, False, foo=1, bar='hello'),
+                               inner_loops=N)
 
 if __name__ == '__main__':
     main()