diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2020-05-25 22:36:44 -0400 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2020-05-28 14:38:56 -0400 |
commit | 77f1b7d236dba6b1c859bb428ef32d118ec372e6 (patch) | |
tree | 7fae8eaaf303d6ce02bd423abf216550001e2f7b /examples | |
parent | 366e88ea0e5c5417184c1dd4776cff752560631d (diff) | |
download | sqlalchemy-77f1b7d236dba6b1c859bb428ef32d118ec372e6.tar.gz |
callcount reductions and refinement for cached queries
This commit includes that we've removed the "_orm_query"
attribute from compile state as well as query context.
The attribute created reference cycles and also added
method call overhead. As part of this change,
the interface for ORMExecuteState changes a bit, as well
as the interface for the horizontal sharding extension
which now deprecates the "query_chooser" callable
in favor of "execute_chooser", which receives the contextual
object. This will also work more nicely when we implement
the new execution path for bulk updates and deletes.
Pre-merge execution options for statement, connection,
arguments all up front in Connection. that way they
can be passed to the before_execute / after_execute events,
and the ExecutionContext doesn't have to merge as second
time. Core execute is pretty close to 1.3 now.
baked wasn't using the new one()/first()/one_or_none() methods,
fixed that.
Convert non-buffered cursor strategy to be a stateless
singleton. inline all the paths by which the strategy
gets chosen, oracle and SQL Server dialects make use of the
already-invoked post_exec() hook to establish the alternate
strategies, and this is actually much nicer than it was before.
Add caching to mapper instance processor for getters.
Identified a reference cycle per query that was showing
up as a lot of gc cleanup, fixed that.
After all that, performance not budging much. Even
test_baked_query now runs with significantly fewer function
calls than 1.3, still 40% slower.
Basically something about the new patterns just makes
this slower and while I've walked a whole bunch of them
back, it hardly makes a dent. that said, the performance
issues are relatively small, in the 20-40% time increase
range, and the new caching feature
does provide for regular ORM and Core queries that
are cached, and they are faster than non-cached.
Change-Id: I7b0b0d8ca550c05f79e82f75cd8eff0bbfade053
Diffstat (limited to 'examples')
-rw-r--r-- | examples/performance/__init__.py | 21 | ||||
-rw-r--r-- | examples/performance/short_selects.py | 17 |
2 files changed, 26 insertions, 12 deletions
diff --git a/examples/performance/__init__.py b/examples/performance/__init__.py index f4f53f0d5..b6c74d7fe 100644 --- a/examples/performance/__init__.py +++ b/examples/performance/__init__.py @@ -19,7 +19,7 @@ individual suites to be run:: $ python -m examples.performance --help usage: python -m examples.performance [-h] [--test TEST] [--dburl DBURL] [--num NUM] [--profile] [--dump] - [--runsnake] [--echo] + [--echo] {bulk_inserts,large_resultsets,single_inserts} @@ -35,7 +35,6 @@ individual suites to be run:: default is module-specific --profile run profiling and dump call counts --dump dump full call profile (implies --profile) - --runsnake invoke runsnakerun (implies --profile) --echo Echo SQL output An example run looks like:: @@ -208,6 +207,7 @@ We can run our new script directly:: """ # noqa import argparse import cProfile +import gc import os import pstats import re @@ -233,6 +233,7 @@ class Profiler(object): self.num = options.num self.echo = options.echo self.sort = options.sort + self.gc = options.gc self.stats = [] @classmethod @@ -305,10 +306,15 @@ class Profiler(object): def _run_test(self, fn): if self._setup: self._setup(self.dburl, self.echo, self.num) + if self.gc: + # gc.set_debug(gc.DEBUG_COLLECTABLE) + gc.set_debug(gc.DEBUG_STATS) if self.profile or self.dump: self._run_with_profile(fn, self.sort) else: self._run_with_time(fn) + if self.gc: + gc.set_debug(0) @classmethod def main(cls): @@ -369,6 +375,9 @@ class Profiler(object): help="print callers as well (implies --dump)", ) parser.add_argument( + "--gc", action="store_true", help="turn on GC debug stats" + ) + parser.add_argument( "--echo", action="store_true", help="Echo SQL output" ) args = parser.parse_args() @@ -432,11 +441,3 @@ class TestResult(object): def _dump_raw(self): self.stats.dump_stats(self.profile.raw) - - def _runsnake(self): - filename = "%s.profile" % self.test.__name__ - try: - self.stats.dump_stats(filename) - os.system("runsnake %s" % filename) - finally: - os.remove(filename) diff --git a/examples/performance/short_selects.py b/examples/performance/short_selects.py index 38bc1508a..2f2b19c17 100644 --- a/examples/performance/short_selects.py +++ b/examples/performance/short_selects.py @@ -69,6 +69,9 @@ def test_orm_query(n): """test a straight ORM query of the full entity.""" session = Session(bind=engine) for id_ in random.sample(ids, n): + # new style + # stmt = future_select(Customer).where(Customer.id == id_) + # session.execute(stmt).scalars().unique().one() session.query(Customer).filter(Customer.id == id_).one() @@ -77,6 +80,11 @@ def test_orm_query_cols_only(n): """test an ORM query of only the entity columns.""" session = Session(bind=engine) for id_ in random.sample(ids, n): + # new style + # stmt = future_select( + # Customer.id, Customer.name, Customer.description + # ).filter(Customer.id == id_) + # session.execute(stmt).scalars().unique().one() session.query(Customer.id, Customer.name, Customer.description).filter( Customer.id == id_ ).one() @@ -90,7 +98,9 @@ def test_cached_orm_query(n): """test new style cached queries of the full entity.""" s = Session(bind=engine) for id_ in random.sample(ids, n): - stmt = s.query(Customer).filter(Customer.id == id_) + # this runs significantly faster + stmt = future_select(Customer).where(Customer.id == id_) + # stmt = s.query(Customer).filter(Customer.id == id_) s.execute(stmt, execution_options={"compiled_cache": cache}).one() @@ -99,9 +109,12 @@ def test_cached_orm_query_cols_only(n): """test new style cached queries of the full entity.""" s = Session(bind=engine) for id_ in random.sample(ids, n): - stmt = s.query( + stmt = future_select( Customer.id, Customer.name, Customer.description ).filter(Customer.id == id_) + # stmt = s.query( + # Customer.id, Customer.name, Customer.description + # ).filter(Customer.id == id_) s.execute(stmt, execution_options={"compiled_cache": cache}).one() |