Files
pyflowx/benchmarks/bench_advanced.py
T

310 lines
10 KiB
Python
Raw 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.
"""高级基准:条件评估/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))
# threadCPU 密集型受 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()