]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
rework the test here to suit 0.8/0.9 mostly, include time tests
authorMike Bayer <mike_mp@zzzcomputing.com>
Fri, 2 Aug 2013 21:37:59 +0000 (17:37 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Fri, 2 Aug 2013 21:37:59 +0000 (17:37 -0400)
test/perf/orm2010.py

index 937e6ddffb1c3223cbd2dc72f2dd119b2d697490..4f36551c02092e36f4fe1ce8e30b276b7bc797d1 100644 (file)
@@ -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)
-
-    __mapper_args__ = {'polymorphic_identity':'grunt'}
+    employer = relationship("Boss", backref="employees",
+                                primaryjoin=Boss.id == employer_id)
+
+    __mapper_args__ = {'polymorphic_identity': 'grunt'}
 
 if os.path.exists('orm2010.db'):
     os.remove('orm2010.db')
@@ -88,101 +62,119 @@ Base.metadata.create_all(engine)
 
 sess = Session(engine)
 
-def runit():
-    # create 1000 Boss objects.
+factor = 10
+
+def runit(status):
+    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")
 
-    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
-                    ))
 
-import cProfile, os, pstats
+    # do some heavier reading
+    for i in range(5):
+        status("Heavy query run #%d" % (i + 1))
 
-filename = "orm2010.profile"
-cProfile.runctx('runit()', globals(), locals(), filename)
-stats = pstats.Stats(filename)
+        report = []
 
-counts_by_methname = dict((key[2], stats.stats[key][0]) for key in stats.stats)
+        # 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
+                        ))
 
-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))
+        sess.close()  # close out the session
 
-#stats.sort_stats('time', 'calls')
-#stats.print_stats()
-os.system("runsnake %s" % filename)
+def run_with_profile():
+    import cProfile
+    import os
+    import pstats
+    filename = "orm2010.profile"
 
-# SQLA Version: 0.7b1
-# Total calls 4956750
-# Total execute calls: 11201
-# Total executemany calls: 101
+    def status(msg):
+        print(msg)
 
-# SQLA Version: 0.6.6
-# Total calls 7963214
-# Total execute calls: 22201
-# Total executemany calls: 0
+    cProfile.runctx('runit(status)', globals(), locals(), filename)
+    stats = pstats.Stats(filename)
 
-# SQLA Version: 0.5.8
-# Total calls 10556480
-# Total execute calls: 22201
-# Total executemany calls: 0
+    counts_by_methname = dict((key[2], stats.stats[key][0]) for key in stats.stats)
 
+    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))
 
+    #stats.sort_stats('time', 'calls')
+    #stats.print_stats()
+    os.system("runsnake %s" % filename)
 
+    # SQLA Version: 0.7b1
+    # Total calls 4956750
+    # Total execute calls: 11201
+    # Total executemany calls: 101
 
+    # SQLA Version: 0.6.6
+    # Total calls 7963214
+    # Total execute calls: 22201
+    # Total executemany calls: 0
 
+    # SQLA Version: 0.5.8
+    # Total calls 10556480
+    # Total execute calls: 22201
+    # Total executemany calls: 0
 
+def run_with_time():
+    import time
+    now = time.time()
 
+    def status(msg):
+        print("%d - %s" % (time.time() - now, msg))
 
+    runit(status)
+    print("Total time: %d" % (time.time() - now))
 
+run_with_time()