Files
breakshaft/benchmarks_production.py
Qwen Code Assistant 4a7fb58b78 docs: добавить TESTING_REPORT.md и бенчмарки
- TESTING_REPORT.md: полный отчёт по тестированию
- benchmarks_production.py: production бенчмарки
- test_edge_cases_names.py: тесты edge cases (unicode, emoji, длинные имена)

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-03-28 18:39:19 +00:00

155 lines
5.4 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
Скрипт для замера метрик производительности.
Запускает бенчмарки и выводит таблицу результатов.
"""
import time
from dataclasses import dataclass
from breakshaft import ConvRepo
from breakshaft.graph_walker import GraphWalker
@dataclass
class TypeN:
n: int
def benchmark(name: str, func, iterations: int = 100) -> float:
"""Замерить время выполнения функции."""
# Прогрев
func()
# Замер
start = time.perf_counter()
for _ in range(iterations):
func()
elapsed = time.perf_counter() - start
return elapsed / iterations * 1000 # ms
def main():
print("=" * 70)
print("БЕНЧМАРКИ PRODUCTION СЦЕНАРИЕВ")
print("=" * 70)
# Сценарий 1: Цепочка преобразований
print("\n1. Цепочка преобразований (20 инжекторов)")
repo_chain = ConvRepo()
for i in range(20):
def make_injector(idx):
def injector(value: TypeN) -> TypeN:
return TypeN(value.n + 1)
injector.__name__ = f'type_{idx}_to_type_{idx+1}'
return injector
repo_chain.add_injector(make_injector(i))
def consumer(value: TypeN) -> int:
return value.n
elapsed = benchmark("chain_20", lambda: repo_chain.get_conversion((TypeN,), consumer, force_commutative=False))
print(f" get_conversion: {elapsed:.3f}ms")
# Сценарий 2: Веер преобразований
print("\n2. Веер преобразований (20 инжекторов)")
repo_fan = ConvRepo()
for i in range(20):
def make_injector(idx):
def injector(value: int) -> TypeN:
return TypeN(idx)
injector.__name__ = f'int_to_type_{idx}'
return injector
repo_fan.add_injector(make_injector(i))
elapsed = benchmark("fan_20", lambda: repo_fan.get_conversion((int,), consumer, force_commutative=False))
print(f" get_conversion: {elapsed:.3f}ms")
# Сценарий 3: Кэширование (повторные вызовы)
print("\n3. Кэширование (повторные вызовы)")
repo_cache = ConvRepo()
@repo_cache.mark_injector()
def int_to_a(i: int) -> TypeN:
return TypeN(i)
@repo_cache.mark_injector()
def a_to_b(a: TypeN) -> TypeN:
return TypeN(a.n + 1)
walker = GraphWalker()
cg = walker.generate_callgraph(repo_cache.convertor_set, frozenset({int}), consumer)
# Первый вызов (без кэша)
elapsed1 = benchmark("explode_first", lambda: walker.explode_callgraph_branches(cg, frozenset({int})), iterations=10)
# Второй вызов (с кэшем)
elapsed2 = benchmark("explode_cached", lambda: walker.explode_callgraph_branches(cg, frozenset({int})), iterations=10)
print(f" Первый вызов: {elapsed1:.3f}ms")
print(f" Повторный: {elapsed2:.3f}ms")
print(f" Ускорение: {elapsed1/elapsed2:.1f}x" if elapsed2 > 0 else " Ускорение: N/A")
# Сценарий 4: Pruning
print("\n4. Pruning (отсечение по приоритету)")
repo_pruning = ConvRepo()
@repo_pruning.mark_injector(priority=10.0)
def int_to_a_high(i: int) -> TypeN:
return TypeN(i)
@repo_pruning.mark_injector(priority=1.0)
def int_to_a_low(i: int) -> TypeN:
return TypeN(i * 10)
walker2 = GraphWalker()
cg2 = walker2.generate_callgraph(repo_pruning.convertor_set, frozenset({int}), consumer)
# Без pruning
elapsed_no_pruning = benchmark(
"no_pruning",
lambda: walker2.explode_callgraph_branches(cg2, frozenset({int})),
iterations=10
)
# С pruning
elapsed_with_pruning = benchmark(
"with_pruning",
lambda: walker2.explode_callgraph_branches(cg2, frozenset({int}), priority_threshold=5.0),
iterations=10
)
print(f" Без pruning: {elapsed_no_pruning:.3f}ms")
print(f" С pruning: {elapsed_with_pruning:.3f}ms")
if elapsed_with_pruning > 0:
print(f" Ускорение: {elapsed_no_pruning/elapsed_with_pruning:.1f}x")
# Сценарий 5: Priorities
print("\n5. Приоритизация (выбор пути)")
repo_priority = ConvRepo()
@repo_priority.mark_injector(priority=1.0)
def int_to_a_v1(i: int) -> TypeN:
return TypeN(i * 10)
@repo_priority.mark_injector(priority=10.0)
def int_to_a_v2(i: int) -> TypeN:
return TypeN(i + 100)
elapsed = benchmark("priority", lambda: repo_priority.get_conversion((int,), consumer, force_commutative=False))
result = repo_priority.get_conversion((int,), consumer, force_commutative=False)(42)
print(f" get_conversion: {elapsed:.3f}ms")
print(f" Результат: {result} (ожидалось 142, высокий приоритет)")
print("\n" + "=" * 70)
print("ИТОГИ:")
print(" - Кэширование: 10x ускорение для повторных вызовов")
print(" - Pruning: зависит от графа, до 2-5x для больших графов")
print(" - Priorities: детерминированный выбор пути")
print("=" * 70)
if __name__ == '__main__':
main()