summaryrefslogtreecommitdiff
path: root/test/perf/orm2010.py
diff options
context:
space:
mode:
Diffstat (limited to 'test/perf/orm2010.py')
-rw-r--r--test/perf/orm2010.py179
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()