Skip to content

Instantly share code, notes, and snippets.

@remram44
Last active August 29, 2015 14:14
Show Gist options
  • Save remram44/f85405f6473a326ad4a3 to your computer and use it in GitHub Desktop.
Save remram44/f85405f6473a326ad4a3 to your computer and use it in GitHub Desktop.
Class generation timing, dynamic vs generated Python
*.py[co]
# Packages
*.egg
*.egg-info
dist
build
eggs
parts
bin
var
sdist
develop-eggs
.installed.cfg
lib
lib64
# Installer logs
pip-log.txt
# Unit test / coverage reports
.coverage
.tox
nosetests.xml
# Eclipse PyDev
.project
.pydevproject
# PyCharm
.idea
# Vagrant
.vagrant
from __future__ import division, print_function, unicode_literals
import os
import random
import sys
import timeit
words = ['stitch', 'tense', 'drum', 'prefer', 'lying', 'wiry', 'reject', 'one',
'bored', 'interrupt', 'ignorant', 'dramatic', 'material', 'separate',
'tremendous', 'furry', 'correct', 'eight', 'geese', 'frame', 'lively',
'loose', 'dark', 'wait', 'zippy', 'pets', 'protest', 'violet', 'help']
def main():
if not os.path.isfile('specs.json'):
with open('specs.json', 'w') as fp:
fp.write('[\n')
for i in range(2000):
number = random.randint(0, 4)
message = ' '.join(random.choice(words) for j in range(3))
name = 'module%d' % (i + 1)
fp.write(' {\n'
' "name": "%s",\n'
' "number": %d,\n'
' "message": "%s"\n'
' }' % (name, number, message))
if i + 1 != 2000:
fp.write(',')
fp.write('\n')
fp.write(']\n')
if len(sys.argv) == 2:
if sys.argv[1] == 'static_gen':
print(timeit.timeit(setup='import gen_static',
stmt='gen_static.generate()',
number=1))
return 0
elif sys.argv[1] == 'static':
print(timeit.timeit(setup='import gen_static',
stmt='gen_static.load()',
number=1))
return 0
elif sys.argv[1] == 'dynamic':
print(timeit.timeit(setup='import gen_dynamic',
stmt='gen_dynamic.load()',
number=1))
return 0
print("Usage: class_gen <static_gen|static|dynamic>", file=sys.stderr)
sys.exit(1)
if __name__ == '__main__':
main()
from __future__ import division, print_function, unicode_literals
import json
class BaseClass(object):
def update(self):
self.compute()
def compute(self):
raise NotImplementedError
def make_compute(nb, s):
def specialized_compute(self):
for i in range(nb):
print(s)
def type_(*args):
return type(*args)
def load():
with open('specs.json', 'rb') as fp:
mod_defs = json.load(fp)
modules = []
for mod_def in mod_defs:
comp = make_compute(mod_def['number'], mod_def['message'])
cl = type_(mod_def['name'].encode('ascii'),
(BaseClass,),
{'compute': comp})
modules.append(cl)
assert len(modules) == 2000
from __future__ import division, print_function, unicode_literals
from jinja2 import Environment, FileSystemLoader
import json
class BaseClass(object):
def update(self):
self.compute()
def compute(self):
raise NotImplementedError
def generate():
env = Environment(loader=FileSystemLoader('.'))
template = env.get_template('mod_template.py.jinja2')
with open('_generated_modules.py', 'w') as fout:
with open('specs.json', 'rb') as fp:
mod_defs = json.load(fp)
names = []
for mod_def in mod_defs:
fout.write(template.render(name=mod_def['name'],
number=mod_def['number'],
message=repr(mod_def['message'])))
fout.write('\n\n\n')
names.append(mod_def['name'])
fout.write('modules = [\n')
for name in names:
fout.write(' %s,\n' % name)
fout.write(']\n')
def load():
from _generated_modules import modules
assert len(modules) == 2000
class {{ name }}({{ base_name }}):
def compute(self):
for i in range({{ number }}):
print({{ message }})
@remram44
Copy link
Author

Initial revision (0bf42acc), timings on Windows 7, Python 2.7.9 x86:

>python class_gen.py dynamic
1.63808020198

>python class_gen.py static_gen
2.32915985317

>python class_gen.py static
0.00492282218999

Very surprising.

@remram44
Copy link
Author

Obviously, this was because the module is loaded only once.

New version (67c52e9), same machine:

>python -B class_gen.py static_gen
0.0472962108979
>python -B class_gen.py static
0.0881224513533
>python -B class_gen.py dynamic
0.0346671535893

The dynamic version is faster here, with type() calls using most of the time:
call graph

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment