Files
archived-MoviePilot/scripts/benchmark_metainfo_rust.py
2026-05-23 17:45:39 +08:00

117 lines
3.8 KiB
Python

import argparse
import statistics
import sys
import time
from contextlib import contextmanager
from pathlib import Path
PROJECT_ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(PROJECT_ROOT))
from app.core import metainfo as metainfo_module
from app.core.metainfo import MetaInfo, MetaInfoPath
from tests.cases.meta import meta_cases
def build_inputs(repeat: int):
"""
构造覆盖 MetaInfo 和 MetaInfoPath 的基准输入。
"""
inputs = []
for _ in range(repeat):
for item in meta_cases:
if item.get("path"):
inputs.append(("path", item["path"], item.get("subtitle")))
else:
inputs.append(("title", item["title"], item.get("subtitle")))
return inputs
def disabled_rust_parse(*_args, **_kwargs):
"""
关闭 Rust MetaInfo 快路径,用于测量旧 Python 链路。
"""
return None
@contextmanager
def selected_meta_parser(use_rust: bool):
"""
在 Rust 入口和 Python 旧实现之间切换。
"""
original_parse = metainfo_module.rust_accel.parse_metainfo
original_parse_path = metainfo_module.rust_accel.parse_metainfo_path
original_find = metainfo_module.rust_accel.find_metainfo
if not use_rust:
metainfo_module.rust_accel.parse_metainfo = disabled_rust_parse
metainfo_module.rust_accel.parse_metainfo_path = disabled_rust_parse
metainfo_module.rust_accel.find_metainfo = disabled_rust_parse
try:
yield
finally:
metainfo_module.rust_accel.parse_metainfo = original_parse
metainfo_module.rust_accel.parse_metainfo_path = original_parse_path
metainfo_module.rust_accel.find_metainfo = original_find
def parse_all(inputs, use_rust: bool):
"""
执行一轮完整 MetaInfo/MetaInfoPath 入口解析。
"""
with selected_meta_parser(use_rust):
parsed = []
for kind, value, subtitle in inputs:
if kind == "path":
parsed.append(MetaInfoPath(Path(value)))
else:
parsed.append(MetaInfo(title=value, subtitle=subtitle, custom_words=["#"]))
return parsed
def measure(inputs, use_rust: bool, loops: int, repeats: int):
"""
多轮测量 MetaInfo 入口解析耗时。
"""
samples = []
parsed_count = 0
for _ in range(repeats):
start = time.perf_counter()
for _ in range(loops):
parsed = parse_all(inputs, use_rust)
parsed_count = len(parsed)
samples.append((time.perf_counter() - start) * 1000 / loops)
return statistics.median(samples), parsed_count
def parse_args():
"""
解析命令行参数。
"""
parser = argparse.ArgumentParser(description="Benchmark MetaInfo parsing through public entries")
parser.add_argument("--repeat-inputs", type=int, default=20, help="Repeat meta cases per loop")
parser.add_argument("--loops", type=int, default=10, help="Loops per repeat")
parser.add_argument("--repeats", type=int, default=5, help="Repeat count")
return parser.parse_args()
def main() -> int:
"""
运行 MetaInfo Rust 与 Python 入口链路基准测试。
"""
args = parse_args()
inputs = build_inputs(args.repeat_inputs)
rust_ms, rust_count = measure(inputs, use_rust=True, loops=args.loops, repeats=args.repeats)
python_ms, python_count = measure(inputs, use_rust=False, loops=args.loops, repeats=args.repeats)
speedup = python_ms / rust_ms if rust_ms else 0
print(f"items_per_loop={len(inputs)} loops={args.loops} repeats={args.repeats}")
print(f"rust_items={rust_count} python_items={python_count}")
print(f"rust_chain_ms_per_loop={rust_ms:.3f}")
print(f"python_chain_ms_per_loop={python_ms:.3f}")
print(f"speedup={speedup:.2f}x")
return 0
if __name__ == "__main__":
raise SystemExit(main())