chore: 2 small benchmark scripts
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import asyncio
|
||||
import tempfile
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
from lrx_cli.authenticators import create_authenticators
|
||||
from lrx_cli.cache import CacheEngine
|
||||
from lrx_cli.config import AppConfig, load_config
|
||||
from lrx_cli.fetchers import BaseFetcher, FetcherMethodType, create_fetchers
|
||||
from lrx_cli.models import TrackMeta
|
||||
|
||||
SAMPLE_TRACK = TrackMeta(
|
||||
title="One Last Kiss",
|
||||
artist="Hikaru Utada",
|
||||
album="One Last Kiss",
|
||||
length=252026,
|
||||
trackid="5RhWszHMSKzb7KiXk4Ae0M",
|
||||
url="https://open.spotify.com/track/5RhWszHMSKzb7KiXk4Ae0M",
|
||||
)
|
||||
|
||||
# Sources that reach out over the network end-to-end; "local" and "cache-search"
|
||||
# have no I/O worth timing.
|
||||
METHODS: list[FetcherMethodType] = [
|
||||
"lrclib",
|
||||
"lrclib-search",
|
||||
"spotify",
|
||||
"musixmatch-spotify",
|
||||
"musixmatch",
|
||||
"netease",
|
||||
"qqmusic",
|
||||
]
|
||||
|
||||
Row = tuple[str, float, str, str]
|
||||
|
||||
|
||||
def _new_runtime(
|
||||
config: AppConfig, db_path: Path
|
||||
) -> dict[FetcherMethodType, BaseFetcher]:
|
||||
cache = CacheEngine(str(db_path))
|
||||
authenticators = create_authenticators(cache, config)
|
||||
return create_fetchers(cache, authenticators, config)
|
||||
|
||||
|
||||
async def _timed(name: str, fetcher: BaseFetcher) -> Row:
|
||||
start = time.perf_counter()
|
||||
try:
|
||||
result = await fetcher.fetch(SAMPLE_TRACK, bypass_cache=True)
|
||||
synced = result.synced.status.name if result.synced else "n/a"
|
||||
unsynced = result.unsynced.status.name if result.unsynced else "n/a"
|
||||
except Exception as exc: # noqa: BLE001
|
||||
synced = unsynced = f"ERR: {exc}"
|
||||
elapsed_ms = (time.perf_counter() - start) * 1000
|
||||
return name, elapsed_ms, synced, unsynced
|
||||
|
||||
|
||||
def _print_table(rows: list[Row]) -> None:
|
||||
name_w = max(max(len(name) for name, _, _, _ in rows), len("source"))
|
||||
synced_w = max(max(len(s) for _, _, s, _ in rows), len("synced"))
|
||||
unsynced_w = max(max(len(u) for _, _, _, u in rows), len("unsynced"))
|
||||
|
||||
print(
|
||||
f"{'source':<{name_w}} {'time(ms)':>10} "
|
||||
f"{'synced':<{synced_w}} {'unsynced':<{unsynced_w}}"
|
||||
)
|
||||
print(
|
||||
"-" * name_w + " " + "-" * 10 + " " + "-" * synced_w + " " + "-" * unsynced_w
|
||||
)
|
||||
for name, elapsed_ms, synced, unsynced in rows:
|
||||
print(
|
||||
f"{name:<{name_w}} {elapsed_ms:>10.1f} "
|
||||
f"{synced:<{synced_w}} {unsynced:<{unsynced_w}}"
|
||||
)
|
||||
|
||||
|
||||
async def run_bench() -> list[Row]:
|
||||
"""Time each fetcher's full `fetch()` pipeline (search/match + retrieval +
|
||||
parsing), bypassing the on-disk cache but nothing else."""
|
||||
with tempfile.TemporaryDirectory(prefix="lrx-bench-") as tmp:
|
||||
fetchers = _new_runtime(load_config(), Path(tmp) / "cred.db")
|
||||
return [await _timed(method, fetchers[method]) for method in METHODS]
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(
|
||||
description=(
|
||||
"Time each source's full fetch() pipeline (search/match + retrieval + "
|
||||
"parsing)."
|
||||
)
|
||||
)
|
||||
parser.parse_args()
|
||||
|
||||
rows = asyncio.run(run_bench())
|
||||
_print_table(rows)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user