"""高级基准:条件评估/YAML 加载/通知器/run_iter/子图/取消/CPU+I/O 密集型任务. 这些基准覆盖核心调度引擎的扩展路径,与 bench_graph/bench_execution/bench_context/ bench_storage 互补,构成完整的性能度量体系。 """ from __future__ import annotations import contextlib import tempfile import time from pathlib import Path from typing import Any import pyflowx as px from benchmarks import print_results, time_it from pyflowx import Graph, TaskSpec from pyflowx.cancellation import CancelToken from pyflowx.conditions import BuiltinConditions from pyflowx.notification import CallbackNotifier, NotificationLevel # ============================================================================ # 基准:条件评估 # ============================================================================ def bench_conditions() -> None: """条件评估基准(静态/上下文/复合).""" results: list[tuple[str, int, float, float]] = [] # 静态条件(IS_LINUX) cond_static = BuiltinConditions.IS_LINUX() ctx: dict[str, Any] = {} ms, ops = time_it(lambda c=cond_static, x=ctx: c(x), iterations=10000, warmup=500) results.append(("static(IS_LINUX)", 10000, ms, ops)) # 上下文条件(DEP_EQUALS) cond_dep = BuiltinConditions.DEP_EQUALS("a", 1) ctx_dep = {"a": 1, "b": 2} ms, ops = time_it(lambda c=cond_dep, x=ctx_dep: c(x), iterations=10000, warmup=500) results.append(("DEP_EQUALS", 10000, ms, ops)) # 复合条件(AND(OR(NOT, DEP_TRUTHY), DEP_PRESENT)) cond_complex = BuiltinConditions.AND( BuiltinConditions.OR( BuiltinConditions.NOT(BuiltinConditions.IS_WINDOWS()), BuiltinConditions.DEP_TRUTHY("a"), ), BuiltinConditions.DEP_PRESENT("b"), ) ms, ops = time_it(lambda c=cond_complex, x=ctx_dep: c(x), iterations=10000, warmup=500) results.append(("AND(OR(NOT,DEP_TRUTHY),DEP_PRESENT)", 10000, ms, ops)) print_results("条件评估", results) # ============================================================================ # 基准:YAML 加载 # ============================================================================ _YAML_TEMPLATE = """\ jobs: {jobs} defaults: retry: max_attempts: 2 backoff: 0.0 """ def _make_yaml(n: int) -> str: """生成 n 个任务的 YAML(链式依赖).""" lines = [] for i in range(n): deps = f"needs: [t{i - 1}]" if i > 0 else "" lines.append(f" t{i}:\n cmd: ['true']\n {deps}".rstrip()) return _YAML_TEMPLATE.format(jobs="\n".join(lines)) def bench_yaml_load() -> None: """YAML 加载基准(解析 + Graph 构建).""" results: list[tuple[str, int, float, float]] = [] tmp_dir = tempfile.mkdtemp() for n in (10, 50, 100): yaml_text = _make_yaml(n) yaml_path = Path(tmp_dir) / f"bench_{n}.yaml" yaml_path.write_text(yaml_text, encoding="utf-8") ms, _ops = time_it(lambda p=yaml_path: Graph.from_yaml(p), iterations=20, warmup=2) results.append((f"yaml_load({n})", 20, ms, 1.0 / (ms / 1000) if ms > 0 else 0)) print_results("YAML 加载 (from_yaml)", results) import shutil shutil.rmtree(tmp_dir, ignore_errors=True) # ============================================================================ # 基准:通知器 # ============================================================================ def bench_notifiers() -> None: """通知器 notify 调用开销基准.""" results: list[tuple[str, int, float, float]] = [] # 构造一个真实 TaskEvent from pyflowx.task import TaskEvent, TaskStatus event = TaskEvent(task="t0", status=TaskStatus.SUCCESS, attempts=1) # CallbackNotifier(无级别过滤) calls: list[int] = [0] def _cb(_e: Any) -> None: calls[0] += 1 notifier = CallbackNotifier(_cb) ms, ops = time_it(lambda e=event, n=notifier: n.notify(e), iterations=50000, warmup=1000) results.append(("CallbackNotifier.notify", 50000, ms, ops)) # 级别过滤(仅 SUCCESS) notifier_filtered = CallbackNotifier(_cb, levels={NotificationLevel.SUCCESS}) ms, ops = time_it(lambda e=event, n=notifier_filtered: n.notify(e), iterations=50000, warmup=1000) results.append(("CallbackNotifier(filtered).notify", 50000, ms, ops)) print_results("通知器 notify", results) # ============================================================================ # 基准:run_iter 流式 API # ============================================================================ def bench_run_iter() -> None: """run_iter 流式执行基准.""" results: list[tuple[str, int, float, float]] = [] def noop() -> None: pass for n in (50, 200, 500): specs = [TaskSpec(f"t{i}", fn=noop) for i in range(n)] graph = Graph.from_specs(specs) def _run_iter(g: Graph = graph) -> None: list(px.run_iter(g, strategy="sequential")) ms, ops = time_it(_run_iter, iterations=10, warmup=2) results.append((f"run_iter(sequential,{n})", 10, ms, ops)) print_results("run_iter 流式执行", results) # ============================================================================ # 基准:子图过滤 # ============================================================================ def bench_subgraph() -> None: """子图过滤基准(subgraph_with_deps 传递闭包计算).""" results: list[tuple[str, int, float, float]] = [] for n in (100, 500, 1000): specs = [] for i in range(n): deps = (f"t{i - 1}",) if i > 0 else () specs.append(TaskSpec(f"t{i}", cmd=["true"], depends_on=deps)) graph = Graph.from_specs(specs) # 取中点任务,触发前半部分传递闭包 target = f"t{n // 2}" ms, ops = time_it( lambda g=graph, t=target: g.subgraph_with_deps([t]), iterations=50, warmup=5, ) results.append((f"subgraph_with_deps({n})", 50, ms, ops)) print_results("子图过滤 (subgraph_with_deps)", results) # ============================================================================ # 基准:取消机制 # ============================================================================ def bench_cancellation() -> None: """取消机制基准(cancel_event 触发到返回).""" results: list[tuple[str, int, float, float]] = [] def slow() -> None: time.sleep(0.5) for n in (50, 200): specs = [TaskSpec(f"t{i}", fn=slow) for i in range(n)] graph = Graph.from_specs(specs) def _cancel_after(g: Graph = graph) -> None: token = CancelToken() # 立即取消,让所有任务变 SKIPPED token.cancel() with contextlib.suppress(Exception): px.run(g, strategy="sequential", cancel_event=token) ms, ops = time_it(_cancel_after, iterations=5, warmup=1) results.append((f"cancel(immediate,{n})", 5, ms, ops)) print_results("取消机制 (cancel_event)", results) # ============================================================================ # 基准:CPU 密集型任务 # ============================================================================ def _fib(n: int) -> int: """递归斐波那契(CPU 密集型).""" if n < 2: return n return _fib(n - 1) + _fib(n - 2) def bench_cpu_intensive() -> None: """CPU 密集型任务基准(递归斐波那契).""" results: list[tuple[str, int, float, float]] = [] for n_tasks, fib_n in ((10, 20), (20, 20), (10, 25)): specs = [TaskSpec(f"t{i}", fn=_fib, args=(fib_n,)) for i in range(n_tasks)] graph = Graph.from_specs(specs) # sequential ms, ops = time_it( lambda g=graph: px.run(g, strategy="sequential"), iterations=3, warmup=1, ) results.append((f"cpu-seq({n_tasks}x fib{fib_n})", 3, ms, ops)) # thread(CPU 密集型受 GIL 限制,验证是否退化) ms, ops = time_it( lambda g=graph: px.run(g, strategy="thread", max_workers=4), iterations=3, warmup=1, ) results.append((f"cpu-thread({n_tasks}x fib{fib_n})", 3, ms, ops)) print_results("CPU 密集型 (递归斐波那契)", results) # ============================================================================ # 基准:I/O 密集型任务 # ============================================================================ def bench_io_intensive() -> None: """I/O 密集型任务基准(sleep 模拟).""" results: list[tuple[str, int, float, float]] = [] def io_sleep() -> None: time.sleep(0.01) # 10ms 模拟 I/O for n in (20, 50): specs = [TaskSpec(f"t{i}", fn=io_sleep) for i in range(n)] graph = Graph.from_specs(specs) # sequential(总耗时 ≈ n * 10ms) ms, ops = time_it( lambda g=graph: px.run(g, strategy="sequential"), iterations=3, warmup=1, ) results.append((f"io-seq({n})", 3, ms, ops)) # thread(I/O 密集型应能并行,总耗时 ≈ 10ms) ms, ops = time_it( lambda g=graph: px.run(g, strategy="thread", max_workers=8), iterations=3, warmup=1, ) results.append((f"io-thread({n})", 3, ms, ops)) # async(I/O 密集型应能并行) ms, ops = time_it( lambda g=graph: px.run(g, strategy="async"), iterations=3, warmup=1, ) results.append((f"io-async({n})", 3, ms, ops)) print_results("I/O 密集型 (sleep 10ms)", results) # ============================================================================ # 主入口 # ============================================================================ def run_advanced() -> None: """运行全部高级基准.""" bench_conditions() bench_yaml_load() bench_notifiers() bench_run_iter() bench_subgraph() bench_cancellation() bench_cpu_intensive() bench_io_intensive()