123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203 |
- # Copyright Amethyst Reese
- # Licensed under the MIT license
- """
- Simple perf tests for aiosqlite and the asyncio run loop.
- """
- import string
- import tempfile
- import time
- from unittest import IsolatedAsyncioTestCase as TestCase
- import aiosqlite
- from .smoke import setup_logger
- TEST_DB = ":memory:"
- TARGET = 2.0
- RESULTS = {}
- def timed(fn, name=None):
- """
- Decorator for perf testing a block of async code.
- Expects the wrapped function to return an async generator.
- The generator should do setup, then yield when ready to start perf testing.
- The decorator will then pump the generator repeatedly until the target
- time has been reached, then close the generator and print perf results.
- """
- name = name or fn.__name__
- async def wrapper(*args, **kwargs):
- gen = fn(*args, **kwargs)
- await gen.asend(None)
- count = 0
- before = time.time()
- while True:
- count += 1
- value = time.time() - before < TARGET
- try:
- if value:
- await gen.asend(value)
- else:
- await gen.aclose()
- break
- except StopAsyncIteration:
- break
- except Exception as e:
- print(f"exception occurred: {e}")
- return
- duration = time.time() - before
- RESULTS[name] = (count, duration)
- return wrapper
- class PerfTest(TestCase):
- @classmethod
- def setUpClass(cls):
- print(f"Running perf tests for at least {TARGET:.1f}s each...")
- setup_logger()
- @classmethod
- def tearDownClass(cls):
- print(f"\n{'Perf Test':<25} Iterations Duration {'Rate':>11}")
- for name in sorted(RESULTS):
- count, duration = RESULTS[name]
- rate = count / duration
- name = name.replace("test_", "")
- print(f"{name:<25} {count:>10} {duration:>7.1f}s {rate:>9.1f}/s")
- @timed
- async def test_connection_memory(self):
- while True:
- yield
- async with aiosqlite.connect(TEST_DB):
- pass
- @timed
- async def test_connection_file(self):
- with tempfile.NamedTemporaryFile(delete=False) as tf:
- path = tf.name
- tf.close()
- async with aiosqlite.connect(path) as db:
- await db.execute(
- "create table perf (i integer primary key asc, k integer)"
- )
- await db.execute("insert into perf (k) values (2), (3)")
- await db.commit()
- while True:
- yield
- async with aiosqlite.connect(path):
- pass
- @timed
- async def test_atomics(self):
- async with aiosqlite.connect(TEST_DB) as db:
- await db.execute("create table perf (i integer primary key asc, k integer)")
- await db.execute("insert into perf (k) values (2), (3)")
- await db.commit()
- while True:
- yield
- async with db.execute("select last_insert_rowid()") as cursor:
- await cursor.fetchone()
- @timed
- async def test_inserts(self):
- async with aiosqlite.connect(TEST_DB) as db:
- await db.execute("create table perf (i integer primary key asc, k integer)")
- await db.commit()
- while True:
- yield
- await db.execute("insert into perf (k) values (1), (2), (3)")
- await db.commit()
- @timed
- async def test_insert_ids(self):
- async with aiosqlite.connect(TEST_DB) as db:
- await db.execute("create table perf (i integer primary key asc, k integer)")
- await db.commit()
- while True:
- yield
- cursor = await db.execute("insert into perf (k) values (1)")
- await cursor.execute("select last_insert_rowid()")
- await cursor.fetchone()
- await db.commit()
- @timed
- async def test_insert_macro_ids(self):
- async with aiosqlite.connect(TEST_DB) as db:
- await db.execute("create table perf (i integer primary key asc, k integer)")
- await db.commit()
- while True:
- yield
- await db.execute_insert("insert into perf (k) values (1)")
- await db.commit()
- @timed
- async def test_select(self):
- async with aiosqlite.connect(TEST_DB) as db:
- await db.execute("create table perf (i integer primary key asc, k integer)")
- for i in range(100):
- await db.execute("insert into perf (k) values (%d)" % (i,))
- await db.commit()
- while True:
- yield
- cursor = await db.execute("select i, k from perf")
- assert len(await cursor.fetchall()) == 100
- @timed
- async def test_select_macro(self):
- async with aiosqlite.connect(TEST_DB) as db:
- await db.execute("create table perf (i integer primary key asc, k integer)")
- for i in range(100):
- await db.execute("insert into perf (k) values (%d)" % (i,))
- await db.commit()
- while True:
- yield
- assert len(await db.execute_fetchall("select i, k from perf")) == 100
- async def test_iterable_cursor_perf(self):
- async with aiosqlite.connect(TEST_DB) as db:
- await db.execute(
- "create table ic_perf ("
- "i integer primary key asc, k integer, a integer, b integer, c char(16))"
- )
- for batch in range(128): # add 128k rows
- r_start = batch * 1024
- await db.executemany(
- "insert into ic_perf (k, a, b, c) values(?, 1, 2, ?)",
- [
- *[
- (i, string.ascii_lowercase)
- for i in range(r_start, r_start + 1024)
- ]
- ],
- )
- await db.commit()
- async def test_perf(chunk_size: int):
- while True:
- async with db.execute("SELECT * FROM ic_perf") as cursor:
- cursor.iter_chunk_size = chunk_size
- async for _ in cursor:
- yield
- for chunk_size in [2**i for i in range(4, 11)]:
- await timed(test_perf, f"iterable_cursor @ {chunk_size}")(chunk_size)
|