test: Execution profiling
Adds cprofile to the testenv:py27, as well as to falcon-bench. In the latter case, add the '-p' switch to profile instead of run timeit. If you would like the profiling info dumped to a data file, set the filename using '-o'. Closes #139
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -26,6 +26,7 @@ pip-log.txt
|
||||
.tox
|
||||
nosetests.xml
|
||||
htmlcov
|
||||
*.dat
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
|
||||
@@ -5,6 +5,7 @@ from __future__ import print_function
|
||||
|
||||
import argparse
|
||||
from collections import defaultdict
|
||||
import cProfile
|
||||
from decimal import Decimal
|
||||
import gc
|
||||
import random
|
||||
@@ -30,6 +31,28 @@ def bench(name, iterations, env):
|
||||
return (name, sec_per_req)
|
||||
|
||||
|
||||
def profile(name, env, output=None):
|
||||
if output:
|
||||
filename = name + '-' + output
|
||||
print('Profiling %s ==> %s' %(name, filename))
|
||||
|
||||
else:
|
||||
filename = None
|
||||
|
||||
title = name + ' profile'
|
||||
print()
|
||||
print('=' * len(title))
|
||||
print(title)
|
||||
print('=' * len(title))
|
||||
|
||||
func = create_bench(name, env)
|
||||
|
||||
gc.collect()
|
||||
code = 'for x in xrange(10000): func()'
|
||||
cProfile.runctx(code, locals(), globals(),
|
||||
sort='tottime', filename=filename)
|
||||
|
||||
|
||||
def create_bench(name, env):
|
||||
srmock = helpers.StartResponseMock()
|
||||
|
||||
@@ -121,9 +144,12 @@ def run(frameworks, repetitions, iterations):
|
||||
|
||||
def main():
|
||||
frameworks = [
|
||||
'flask', 'werkzeug', 'falcon',
|
||||
'pecan', 'bottle', 'falcon-ext'
|
||||
|
||||
'bottle',
|
||||
'falcon',
|
||||
'falcon-ext',
|
||||
'flask',
|
||||
'pecan',
|
||||
'werkzeug'
|
||||
]
|
||||
|
||||
parser = argparse.ArgumentParser(description="Falcon benchmark runner")
|
||||
@@ -131,25 +157,32 @@ def main():
|
||||
choices=frameworks, dest='frameworks')
|
||||
parser.add_argument('-i', '--iterations', type=int, default=50000)
|
||||
parser.add_argument('-r', '--repetitions', type=int, default=3)
|
||||
parser.add_argument('-p', '--profile', action='store_true')
|
||||
parser.add_argument('-o', '--profile-output', type=str, default=None)
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.frameworks:
|
||||
frameworks = args.frameworks
|
||||
|
||||
datasets = run(frameworks, args.repetitions, args.iterations)
|
||||
if args.profile:
|
||||
for name in frameworks:
|
||||
profile(name, get_env(name), args.profile_output)
|
||||
|
||||
dataset = consolidate_datasets(datasets)
|
||||
dataset = sorted(dataset, key=lambda r: r[1])
|
||||
baseline = dataset[-1][1]
|
||||
else:
|
||||
datasets = run(frameworks, args.repetitions, args.iterations)
|
||||
|
||||
print('\nResults:\n')
|
||||
dataset = consolidate_datasets(datasets)
|
||||
dataset = sorted(dataset, key=lambda r: r[1])
|
||||
baseline = dataset[-1][1]
|
||||
|
||||
for i, (name, sec_per_req) in enumerate(dataset):
|
||||
req_per_sec = round_to_int(Decimal(1) / sec_per_req)
|
||||
us_per_req = (sec_per_req * Decimal(10 ** 6))
|
||||
factor = round_to_int(baseline / sec_per_req)
|
||||
print('\nResults:\n')
|
||||
|
||||
print('{3}. {0:.<15s}{1:.>06,d} req/sec or {2: >3.2f} μs/req ({4}x)'.
|
||||
format(name, req_per_sec, us_per_req, i + 1, factor))
|
||||
for i, (name, sec_per_req) in enumerate(dataset):
|
||||
req_per_sec = round_to_int(Decimal(1) / sec_per_req)
|
||||
us_per_req = (sec_per_req * Decimal(10 ** 6))
|
||||
factor = round_to_int(baseline / sec_per_req)
|
||||
|
||||
print('')
|
||||
print('{3}. {0:.<15s}{1:.>06,d} req/sec or {2: >3.2f} μs/req ({4}x)'.
|
||||
format(name, req_per_sec, us_per_req, i + 1, factor))
|
||||
|
||||
print()
|
||||
|
||||
@@ -3,6 +3,8 @@ tag_build = dev
|
||||
|
||||
[nosetests]
|
||||
where = falcon/tests
|
||||
verbosity = 2
|
||||
|
||||
with-coverage = true
|
||||
cover-min-percentage = 100
|
||||
cover-package = falcon
|
||||
@@ -11,5 +13,3 @@ cover-html-dir = htmlcov
|
||||
cover-erase = true
|
||||
cover-inclusive = true
|
||||
cover-branches = true
|
||||
verbosity = 2
|
||||
|
||||
|
||||
7
setup.py
7
setup.py
@@ -41,7 +41,6 @@ else:
|
||||
cmdclass = {}
|
||||
ext_modules = []
|
||||
|
||||
|
||||
setup(
|
||||
name='falcon',
|
||||
version=VERSION,
|
||||
@@ -77,8 +76,10 @@ setup(
|
||||
install_requires=REQUIRES,
|
||||
cmdclass=cmdclass,
|
||||
ext_modules=ext_modules,
|
||||
setup_requires=[],
|
||||
entry_points={
|
||||
'console_scripts':
|
||||
['falcon-bench = falcon.cmd.bench:main']
|
||||
'console_scripts': [
|
||||
'falcon-bench = falcon.cmd.bench:main'
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
10
tox.ini
10
tox.ini
@@ -6,6 +6,16 @@ deps = -r{toxinidir}/tools/test-requires
|
||||
commands = {toxinidir}/tools/clean_cythoned.sh {toxinidir}/falcon
|
||||
nosetests {posargs}
|
||||
|
||||
[testenv:py27]
|
||||
deps = -r{toxinidir}/tools/test-requires
|
||||
nose-cprof
|
||||
commands = {toxinidir}/tools/clean_cythoned.sh {toxinidir}/falcon
|
||||
nosetests \
|
||||
--with-cprofile \
|
||||
--cprofile-stats-erase \
|
||||
--cprofile-stats-file=cprofile.dat \
|
||||
{posargs}
|
||||
|
||||
[testenv:py3kwarn]
|
||||
deps = py3kwarn
|
||||
commands = py3kwarn falcon
|
||||
|
||||
Reference in New Issue
Block a user