diff options
Diffstat (limited to 'test/perf/orm2010.py')
-rw-r--r-- | test/perf/orm2010.py | 179 |
1 files changed, 87 insertions, 92 deletions
diff --git a/test/perf/orm2010.py b/test/perf/orm2010.py index 937e6ddff..8036ac268 100644 --- a/test/perf/orm2010.py +++ b/test/perf/orm2010.py @@ -1,7 +1,7 @@ -# monkeypatch the "cdecimal" library in. -# this is a drop-in replacement for "decimal". -# All SQLA versions support cdecimal except -# for the MS-SQL dialect, which is fixed in 0.7 +import warnings +warnings.filterwarnings("ignore", r".*Decimal objects natively") + +# speed up cdecimal if available try: import cdecimal import sys @@ -13,11 +13,7 @@ from sqlalchemy import __version__ from sqlalchemy import Column, Integer, create_engine, ForeignKey, \ String, Numeric -if __version__ < "0.6": - from sqlalchemy.orm.session import Session - from sqlalchemy.orm import relation as relationship -else: - from sqlalchemy.orm import Session, relationship +from sqlalchemy.orm import Session, relationship from sqlalchemy.ext.declarative import declarative_base import random @@ -33,7 +29,7 @@ class Employee(Base): name = Column(String(100), nullable=False) type = Column(String(50), nullable=False) - __mapper_args__ = {'polymorphic_on':type} + __mapper_args__ = {'polymorphic_on': type} class Boss(Employee): __tablename__ = 'boss' @@ -41,7 +37,7 @@ class Boss(Employee): id = Column(Integer, ForeignKey('employee.id'), primary_key=True) golf_average = Column(Numeric) - __mapper_args__ = {'polymorphic_identity':'boss'} + __mapper_args__ = {'polymorphic_identity': 'boss'} class Grunt(Employee): __tablename__ = 'grunt' @@ -51,32 +47,10 @@ class Grunt(Employee): employer_id = Column(Integer, ForeignKey('boss.id')) - # Configure an 'employer' relationship, where Grunt references - # Boss. This is a joined-table subclass to subclass relationship, - # which is a less typical case. - - # In 0.7, "Boss.id" is the "id" column of "boss", as would be expected. - if __version__ >= "0.7": - employer = relationship("Boss", backref="employees", - primaryjoin=Boss.id==employer_id) - - # Prior to 0.7, "Boss.id" is the "id" column of "employee". - # Long story. So we hardwire the relationship against the "id" - # column of Boss' table. - elif __version__ >= "0.6": - employer = relationship("Boss", backref="employees", - primaryjoin=Boss.__table__.c.id==employer_id) - - # In 0.5, the many-to-one loader wouldn't recognize the above as a - # simple "identity map" fetch. So to give 0.5 a chance to emit - # the same amount of SQL as 0.6, we hardwire the relationship against - # "employee.id" to work around the bug. - else: - employer = relationship("Boss", backref="employees", - primaryjoin=Employee.__table__.c.id==employer_id, - foreign_keys=employer_id) + employer = relationship("Boss", backref="employees", + primaryjoin=Boss.id == employer_id) - __mapper_args__ = {'polymorphic_identity':'grunt'} + __mapper_args__ = {'polymorphic_identity': 'grunt'} if os.path.exists('orm2010.db'): os.remove('orm2010.db') @@ -88,101 +62,122 @@ Base.metadata.create_all(engine) sess = Session(engine) -def runit(): - # create 1000 Boss objects. +def runit(status, factor=1): + num_bosses = 100 * factor + num_grunts = num_bosses * 100 + bosses = [ Boss( name="Boss %d" % i, golf_average=Decimal(random.randint(40, 150)) ) - for i in range(1000) + for i in range(num_bosses) ] sess.add_all(bosses) + status("Added %d boss objects" % num_bosses) - - # create 10000 Grunt objects. grunts = [ Grunt( name="Grunt %d" % i, savings=Decimal(random.randint(5000000, 15000000) / 100) ) - for i in range(10000) + for i in range(num_grunts) ] + status("Added %d grunt objects" % num_grunts) - # Assign each Grunt a Boss. Look them up in the DB - # to simulate a little bit of two-way activity with the - # DB while we populate. Autoflush occurs on each query. - # In 0.7 executemany() is used for all the "boss" and "grunt" - # tables since priamry key fetching is not needed. while grunts: + # this doesn't associate grunts with bosses evenly, + # just associates lots of them with a relatively small + # handful of bosses + batch_size = 100 + batch_num = (num_grunts - len(grunts)) / batch_size boss = sess.query(Boss).\ - filter_by(name="Boss %d" % (101 - len(grunts) / 100)).\ + filter_by(name="Boss %d" % batch_num).\ first() - for grunt in grunts[0:100]: + for grunt in grunts[0:batch_size]: grunt.employer = boss - grunts = grunts[100:] + grunts = grunts[batch_size:] sess.commit() + status("Associated grunts w/ bosses and committed") + + # do some heavier reading + for i in range(int(round(factor / 2.0))): + status("Heavy query run #%d" % (i + 1)) - report = [] + report = [] - # load all the Grunts, print a report with their name, stats, - # and their bosses' stats. - for grunt in sess.query(Grunt): - # here, the overhead of a many-to-one fetch of - # "grunt.employer" directly from the identity map - # is less than half of that of 0.6. - report.append(( - grunt.name, - grunt.savings, - grunt.employer.name, - grunt.employer.golf_average - )) + # load all the Grunts, print a report with their name, stats, + # and their bosses' stats. + for grunt in sess.query(Grunt): + report.append(( + grunt.name, + grunt.savings, + grunt.employer.name, + grunt.employer.golf_average + )) -import cProfile, os, pstats + sess.close() # close out the session -filename = "orm2010.profile" -cProfile.runctx('runit()', globals(), locals(), filename) -stats = pstats.Stats(filename) +def run_with_profile(runsnake=False, dump=False): + import cProfile + import pstats + filename = "orm2010.profile" -counts_by_methname = dict((key[2], stats.stats[key][0]) for key in stats.stats) + def status(msg): + print(msg) -print("SQLA Version: %s" % __version__) -print("Total calls %d" % stats.total_calls) -print("Total cpu seconds: %.2f" % stats.total_tt) -print('Total execute calls: %d' \ - % counts_by_methname["<method 'execute' of 'sqlite3.Cursor' " - "objects>"]) -print('Total executemany calls: %d' \ - % counts_by_methname.get("<method 'executemany' of 'sqlite3.Cursor' " - "objects>", 0)) + cProfile.runctx('runit(status)', globals(), locals(), filename) + stats = pstats.Stats(filename) -#stats.sort_stats('time', 'calls') -#stats.print_stats() -os.system("runsnake %s" % filename) + counts_by_methname = dict((key[2], stats.stats[key][0]) for key in stats.stats) -# SQLA Version: 0.7b1 -# Total calls 4956750 -# Total execute calls: 11201 -# Total executemany calls: 101 + print("SQLA Version: %s" % __version__) + print("Total calls %d" % stats.total_calls) + print("Total cpu seconds: %.2f" % stats.total_tt) + print('Total execute calls: %d' \ + % counts_by_methname["<method 'execute' of 'sqlite3.Cursor' " + "objects>"]) + print('Total executemany calls: %d' \ + % counts_by_methname.get("<method 'executemany' of 'sqlite3.Cursor' " + "objects>", 0)) -# SQLA Version: 0.6.6 -# Total calls 7963214 -# Total execute calls: 22201 -# Total executemany calls: 0 + if dump: + stats.sort_stats('time', 'calls') + stats.print_stats() -# SQLA Version: 0.5.8 -# Total calls 10556480 -# Total execute calls: 22201 -# Total executemany calls: 0 + if runsnake: + os.system("runsnake %s" % filename) +def run_with_time(): + import time + now = time.time() + def status(msg): + print("%d - %s" % (time.time() - now, msg)) + runit(status, 10) + print("Total time: %d" % (time.time() - now)) +if __name__ == '__main__': + import argparse + parser = argparse.ArgumentParser() + parser.add_argument('--profile', action='store_true', + help='run shorter test suite w/ cprofilng') + parser.add_argument('--dump', action='store_true', + help='dump full call profile (implies --profile)') + parser.add_argument('--runsnake', action='store_true', + help='invoke runsnakerun (implies --profile)') + args = parser.parse_args() + args.profile = args.profile or args.dump or args.runsnake + if args.profile: + run_with_profile(runsnake=args.runsnake, dump=args.dump) + else: + run_with_time() |