MonkeyCode性能测试与调优:让AI生成的代码跑得更快

代码跑得慢,AI也救不了

MonkeyCode能生成功能正确的代码,但性能不一定最优。N+1查询、内存泄漏、算法复杂度高……这些问题AI不一定能自动避免。

这篇文章用MonkeyCode生成性能测试和调优方案,覆盖:基准测试、Profiling、内存分析、并发压测。

给MonkeyCode的统一Prompt

用Python实现完整的性能测试与调优工具链,要求:
1. 基准测试(Benchmark)框架
2. 函数级Profiling(cProfile + line_profiler)
3. 内存分析(memory_profiler + tracemalloc)
4. 并发压测(Locust + 自定义压测)
5. 性能回归测试(CI/CD集成)
6. 自动生成性能报告(Markdown + HTML)
7. 性能瓶颈自动诊断

需要完整可运行的代码,包含实际案例(Web API、数据处理、并发场景)

1. 基准测试框架

# benchmark_framework.py - MonkeyCode生成
import time
import statistics
import functools
from dataclasses import dataclass, field
from typing import Callable, Any, List, Dict
import pickle
import json

@dataclass
class BenchmarkResult:
    name: str
    mean: float
    median: float
    stdev: float
    min: float
    max: float
    iterations: int
    warmup_iterations: int
    metadata: Dict[str, Any] = field(default_factory=dict)

class Benchmark:
    """基准测试框架"""
    
    def __init__(self, warmup: int = 10, iterations: int = 100):
        self.warmup = warmup
        self.iterations = iterations
        self.results: List[BenchmarkResult] = []
    
    def run(self, name: str, func: Callable, *args, **kwargs) -> BenchmarkResult:
        """运行基准测试"""
        print(f"Running benchmark: {name}")
        
        # 预热
        for _ in range(self.warmup):
            func(*args, **kwargs)
        
        # 正式测试
        times = []
        for _ in range(self.iterations):
            start = time.perf_counter()
            result = func(*args, **kwargs)
            end = time.perf_counter()
            times.append((end - start) * 1000)  # 转换为毫秒
        
        # 统计
        result = BenchmarkResult(
            name=name,
            mean=statistics.mean(times),
            median=statistics.median(times),
            stdev=statistics.stdev(times) if len(times) > 1 else 0,
            min=min(times),
            max=max(times),
            iterations=self.iterations,
            warmup_iterations=self.warmup,
        )
        
        self.results.append(result)
        self._print_result(result)
        
        return result
    
    def _print_result(self, result: BenchmarkResult):
        print(f"  Mean:   {result.mean:.3f} ms")
        print(f"  Median: {result.median:.3f} ms")
        print(f"  StDev:  {result.stdev:.3f} ms")
        print(f"  Min:    {result.min:.3f} ms")
        print(f"  Max:    {result.max:.3f} ms")
        print()
    
    def compare(self, baseline: BenchmarkResult, current: BenchmarkResult) -> float:
        """对比两个基准测试结果,返回加速比"""
        speedup = baseline.mean / current.mean
        print(f"Speedup: {speedup:.2f}x (baseline: {baseline.name}, current: {current.name})")
        return speedup
    
    def save_results(self, filepath: str):
        """保存结果到JSON"""
        data = [
            {
                "name": r.name,
                "mean_ms": r.mean,
                "median_ms": r.median,
                "stdev_ms": r.stdev,
                "min_ms": r.min,
                "max_ms": r.max,
                "iterations": r.iterations,
                "metadata": r.metadata,
            }
            for r in self.results
        ]
        
        with open(filepath, 'w') as f:
            json.dump(data, f, indent=2)
        
        print(f"Results saved to {filepath}")
    
    def generate_report(self, output_path: str = "benchmark_report.html"):
        """生成HTML报告"""
        html = """
        <!DOCTYPE html>
        <html>
        <head>
            <title>Benchmark Report</title>
            <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
            <style>
                body { font-family: sans-serif; margin: 20px; }
                table { border-collapse: collapse; width: 100%; }
                th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
                th { background-color: #f2f2f2; }
                .chart-container { width: 800px; margin: 20px 0; }
            </style>
        </head>
        <body>
            <h1>Benchmark Report</h1>
            <table>
                <tr>
                    <th>Name</th>
                    <th>Mean (ms)</th>
                    <th>Median (ms)</th>
                    <th>StDev (ms)</th>
                    <th>Min (ms)</th>
                    <th>Max (ms)</th>
                </tr>
        """
        
        for r in self.results:
            html += f"""
                <tr>
                    <td>{r.name}</td>
                    <td>{r.mean:.3f}</td>
                    <td>{r.median:.3f}</td>
                    <td>{r.stdev:.3f}</td>
                    <td>{r.min:.3f}</td>
                    <td>{r.max:.3f}</td>
                </tr>
            """
        
        html += """
            </table>
            <div class="chart-container">
                <canvas id="benchmarkChart"></canvas>
            </div>
            <script>
                const ctx = document.getElementById('benchmarkChart').getContext('2d');
                const chart = new Chart(ctx, {
                    type: 'bar',
                    data: {
                        labels: """ + str([r.name for r in self.results]) + """,
                        datasets: [{
                            label: 'Mean (ms)',
                            data: """ + str([r.mean for r in self.results]) + """,
                            backgroundColor: 'rgba(54, 162, 235, 0.5)',
                        }]
                    },
                    options: {
                        responsive: true,
                        scales: {
                            y: { beginAtZero: true }
                        }
                    }
                });
            </script>
        </body>
        </html>
        """
        
        with open(output_path, 'w') as f:
            f.write(html)
        
        print(f"Report generated: {output_path}")

# 装饰器版本
def benchmark(name: str = None, iterations: int = 100, warmup: int = 10):
    """基准测试装饰器"""
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            bench = Benchmark(warmup=warmup, iterations=iterations)
            return bench.run(name or func.__name__, func, *args, **kwargs)
        return wrapper
    return decorator

# 使用示例
if __name__ == '__main__':
    bench = Benchmark(warmup=5, iterations=50)
    
    # 测试JSON序列化性能
    data = {"key": "value", "number": 42, "list": list(range(100))}
    
    json_result = bench.run("json_dumps", lambda: json.dumps(data))
    pickle_result = bench.run("pickle_dumps", lambda: pickle.dumps(data))
    
    bench.compare(json_result, pickle_result)
    bench.generate_report()

2. 函数级Profiling

# profiling.py - MonkeyCode生成
import cProfile
import pstats
import io
import line_profiler
import functools
from typing import Callable, List
import time

class Profiler:
    """性能分析工具"""
    
    @staticmethod
    def profile_cprofile(func: Callable, *args, **kwargs) -> pstats.Stats:
        """使用cProfile进行性能分析"""
        profiler = cProfile.Profile()
        profiler.enable()
        
        result = func(*args, **kwargs)
        
        profiler.disable()
        
        # 输出结果
        stream = io.StringIO()
        stats = pstats.Stats(profiler, stream=stream)
        stats.sort_stats(pstats.SortKey.CUMULATIVE)
        stats.print_stats(20)  # 显示前20个函数
        
        print(stream.getvalue())
        
        return stats
    
    @staticmethod
    def profile_line(func: Callable, *args, **kwargs):
        """使用line_profiler进行逐行分析"""
        # 需要安装: pip install line_profiler
        try:
            from line_profiler import LineProfiler
            
            profiler = LineProfiler()
            profiler.add_function(func)
            
            profiler.enable()
            result = func(*args, **kwargs)
            profiler.disable()
            
            # 输出结果
            profiler.print_stats()
            
        except ImportError:
            print("line_profiler not installed. Install with: pip install line_profiler")
    
    @staticmethod
    def time_it(func: Callable, *args, iterations: int = 1000, **kwargs) -> float:
        """简单计时"""
        start = time.perf_counter()
        for _ in range(iterations):
            func(*args, **kwargs)
        end = time.perf_counter()
        
        avg_time = (end - start) / iterations * 1000  # 毫秒
        print(f"{func.__name__}: {avg_time:.3f} ms/iteration (avg over {iterations} iterations)")
        return avg_time

# 装饰器版本
def profile(func: Callable = None, *, cprofile: bool = True, line_profile: bool = False):
    """性能分析装饰器"""
    def decorator(f):
        @functools.wraps(f)
        def wrapper(*args, **kwargs):
            if cprofile:
                print(f"\n{'='*60}")
                print(f"Profiling: {f.__name__}")
                print(f"{'='*60}")
                Profiler.profile_cprofile(f, *args, **kwargs)
            
            if line_profile:
                print(f"\n{'='*60}")
                print(f"Line Profiling: {f.__name__}")
                print(f"{'='*60}")
                Profiler.profile_line(f, *args, **kwargs)
            
            return f(*args, **kwargs)
        return wrapper
    
    if func is None:
        return decorator
    else:
        return decorator(func)

# 使用示例
@profile(cprofile=True, line_profile=False)
def expensive_operation(n: int = 10000):
    """模拟耗时操作"""
    result = []
    for i in range(n):
        result.append(i ** 2)
    return result

@profile(line_profile=True)
def process_data(data: List[int]):
    """处理数据(逐行分析)"""
    # 查找所有偶数
    evens = [x for x in data if x % 2 == 0]
    
    # 计算平方和
    squares = [x ** 2 for x in evens]
    
    # 求和
    total = sum(squares)
    
    return total

if __name__ == '__main__':
    # cProfile分析
    data = list(range(10000))
    result = process_data(data)
    
    # 简单计时
    Profiler.time_it(expensive_operation, 1000, iterations=100)

3. 内存分析

# memory_profiler.py - MonkeyCode生成
import tracemalloc
import memory_profiler
from memory_profiler import profile as memory_profile
import psutil
import os
from typing import Callable
import gc

class MemoryAnalyzer:
    """内存分析工具"""
    
    @staticmethod
    def analyze_tracemalloc(func: Callable, *args, **kwargs):
        """使用tracemalloc追踪内存分配"""
        tracemalloc.start()
        
        result = func(*args, **kwargs)
        
        snapshot = tracemalloc.take_snapshot()
        top_stats = snapshot.statistics('lineno')
        
        print("\n[Top 10 Memory Allocations]")
        for stat in top_stats[:10]:
            print(stat)
        
        tracemalloc.stop()
        
        return result
    
    @staticmethod
    def measure_peak_memory(func: Callable, *args, **kwargs) -> float:
        """测量函数执行期间的内存峰值(MB)"""
        process = psutil.Process(os.getpid())
        
        # 清理内存
        gc.collect()
        mem_before = process.memory_info().rss / 1024 / 1024  # MB
        
        result = func(*args, **kwargs)
        
        mem_after = process.memory_info().rss / 1024 / 1024  # MB
        mem_peak = mem_after - mem_before
        
        print(f"Peak memory usage: {mem_peak:.2f} MB")
        return mem_peak
    
    @staticmethod
    def detect_memory_leak(func: Callable, iterations: int = 100, *args, **kwargs):
        """检测内存泄漏"""
        process = psutil.Process(os.getpid())
        
        mem_usage = []
        
        for i in range(iterations):
            gc.collect()  # 强制垃圾回收
            mem_before = process.memory_info().rss / 1024 / 1024
            
            result = func(*args, **kwargs)
            
            mem_after = process.memory_info().rss / 1024 / 1024
            mem_usage.append(mem_after)
            
            if i % 10 == 0:
                print(f"Iteration {i}: {mem_after:.2f} MB")
        
        # 检查是否泄漏
        if len(mem_usage) > 10:
            trend = (mem_usage[-1] - mem_usage[0]) / len(mem_usage)
            if trend > 0.1:  # 每次迭代增长超过0.1MB
                print(f"⚠️  Potential memory leak detected! Trend: {trend:.2f} MB/iteration")
            else:
                print(f"✅ No memory leak detected. Trend: {trend:.2f} MB/iteration")
    
    @staticmethod
    def get_object_counts():
        """获取各类型对象数量(调试内存问题)"""
        import collections
        
        counts = collections.Counter(type(obj).__name__ for obj in gc.get_objects())
        
        print("\n[Top 20 Object Types]")
        for obj_type, count in counts.most_common(20):
            print(f"  {obj_type}: {count}")

# 装饰器版本
def memory_track(func: Callable = None, *, detailed: bool = False):
    """内存追踪装饰器"""
    def decorator(f):
        @functools.wraps(f)
        def wrapper(*args, **kwargs):
            if detailed:
                print(f"\n{'='*60}")
                print(f"Memory Tracking (detailed): {f.__name__}")
                print(f"{'='*60}")
                MemoryAnalyzer.analyze_tracemalloc(f, *args, **kwargs)
            else:
                print(f"\n{'='*60}")
                print(f"Memory Tracking: {f.__name__}")
                print(f"{'='*60}")
                MemoryAnalyzer.measure_peak_memory(f, *args, **kwargs)
            
            return f(*args, **kwargs)
        return wrapper
    
    if func is None:
        return decorator
    else:
        return decorator(func)

# 使用示例
@memory_track(detailed=False)
def create_large_list(size: int = 1000000):
    """创建大列表"""
    return [i for i in range(size)]

@memory_profile  # 需要安装: pip install memory_profiler
def process_large_data():
    """处理大量数据"""
    data = [i ** 2 for i in range(100000)]
    filtered = [x for x in data if x % 3 == 0]
    return sum(filtered)

if __name__ == '__main__':
    # 测量内存峰值
    result = create_large_list(1000000)
    
    # 检测内存泄漏
    def leaky_func():
        # 模拟内存泄漏(全局列表不断增长)
        global _leak_list
        if not hasattr(sys.modules[__name__], '_leak_list'):
            _leak_list = []
        _leak_list.extend([1] * 10000)
    
    print("\n[Memory Leak Detection]")
    MemoryAnalyzer.detect_memory_leak(leaky_func, iterations=50)
    
    # 获取对象统计
    MemoryAnalyzer.get_object_counts()

4. 并发压测

# load_testing.py - MonkeyCode生成
import asyncio
import aiohttp
import time
import statistics
from typing import List, Dict, Any
import json
import concurrent.futures
import threading

class LoadTester:
    """并发压测工具"""
    
    def __init__(self, base_url: str, concurrency: int = 10, total_requests: int = 1000):
        self.base_url = base_url
        self.concurrency = concurrency
        self.total_requests = total_requests
        self.results: List[float] = []
        self.errors: List[str] = []
        self.status_codes: Dict[int, int] = {}
    
    async def single_request(self, session: aiohttp.ClientSession, endpoint: str) -> float:
        """发送单个请求,返回响应时间(毫秒)"""
        url = f"{self.base_url}{endpoint}"
        
        start = time.perf_counter()
        try:
            async with session.get(url, timeout=aiohttp.ClientTimeout(total=10)) as response:
                await response.text()
                status = response.status
                
                self.status_codes[status] = self.status_codes.get(status, 0) + 1
                
                elapsed = (time.perf_counter() - start) * 1000
                return elapsed
        except Exception as e:
            self.errors.append(str(e))
            return -1
    
    async def run_load_test(self, endpoint: str = "/"):
        """运行压测"""
        print(f"Starting load test: {self.total_requests} requests, concurrency: {self.concurrency}")
        
        connector = aiohttp.TCPConnector(limit=self.concurrency * 2)
        async with aiohttp.ClientSession(connector=connector) as session:
            tasks = []
            for _ in range(self.total_requests):
                task = self.single_request(session, endpoint)
                tasks.append(task)
            
            # 限制并发数
            semaphore = asyncio.Semaphore(self.concurrency)
            
            async def bounded_task(task):
                async with semaphore:
                    return await task
            
            bounded_tasks = [bounded_task(t) for t in tasks]
            
            start_time = time.perf_counter()
            results = await asyncio.gather(*bounded_tasks)
            end_time = time.perf_counter()
        
        # 统计结果
        self.results = [r for r in results if r > 0]
        total_time = (end_time - start_time) * 1000  # 毫秒
        
        self._print_statistics(total_time)
    
    def _print_statistics(self, total_time: float):
        """打印统计信息"""
        if not self.results:
            print("No successful requests!")
            return
        
        print(f"\n{'='*60}")
        print(f"Load Test Results")
        print(f"{'='*60}")
        print(f"Total Requests: {self.total_requests}")
        print(f"Successful: {len(self.results)}")
        print(f"Failed: {len(self.errors)}")
        print(f"Total Time: {total_time/1000:.2f} seconds")
        print(f"Requests/Second (RPS): {len(self.results) / (total_time/1000):.2f}")
        print(f"Mean Response Time: {statistics.mean(self.results):.2f} ms")
        print(f"Median Response Time: {statistics.median(self.results):.2f} ms")
        print(f"Min Response Time: {min(self.results):.2f} ms")
        print(f"Max Response Time: {max(self.results):.2f} ms")
        
        if len(self.results) > 1:
            print(f"StdDev Response Time: {statistics.stdev(self.results):.2f} ms")
        
        print(f"\nStatus Code Distribution:")
        for status, count in sorted(self.status_codes.items()):
            print(f"  {status}: {count} ({count/len(self.results)*100:.1f}%)")
        
        if self.errors:
            print(f"\nErrors (first 5):")
            for err in self.errors[:5]:
                print(f"  {err}")
    
    def save_results(self, filepath: str):
        """保存结果到JSON"""
        data = {
            "total_requests": self.total_requests,
            "successful": len(self.results),
            "failed": len(self.errors),
            "rps": len(self.results) / (sum(self.results) / 1000),
            "mean_ms": statistics.mean(self.results),
            "median_ms": statistics.median(self.results),
            "min_ms": min(self.results),
            "max_ms": max(self.results),
            "status_codes": self.status_codes,
            "errors": self.errors[:10],  # 只保存前10个错误
        }
        
        with open(filepath, 'w') as f:
            json.dump(data, f, indent=2)
        
        print(f"\nResults saved to {filepath}")

# 使用Locust进行更复杂的压测
try:
    from locust import HttpUser, task, between
    
    class WebsiteUser(HttpUser):
        """Locust压测用户行为模拟"""
        wait_time = between(1, 3)  # 每个用户请求间隔1-3秒
        
        @task(3)  # 权重:3倍于其他任务
        def view_homepage(self):
            self.client.get("/")
        
        @task(1)
        def view_api(self):
            self.client.get("/api/users")
        
        @task(1)
        def create_user(self):
            self.client.post("/api/users", json={
                "name": "Test User",
                "email": "test@example.com"
            })
except ImportError:
    print("Locust not installed. Install with: pip install locust")

if __name__ == '__main__':
    # 使用LoadTester
    tester = LoadTester(
        base_url="http://localhost:8000",
        concurrency=50,
        total_requests=1000
    )
    
    asyncio.run(tester.run_load_test(endpoint="/api/data"))
    tester.save_results("load_test_results.json")

5. 性能回归测试

# performance_regression.py - MonkeyCode生成
import json
import subprocess
import sys
from pathlib import Path
from typing import Dict, List, Optional
import difflib

class PerformanceRegressionTest:
    """性能回归测试"""
    
    def __init__(self, baseline_file: str = "baseline_performance.json"):
        self.baseline_file = Path(baseline_file)
        self.baseline: Dict[str, float] = {}
        self.current: Dict[str, float] = {}
        self.threshold_percent: float = 10.0  # 性能下降超过10%则报警
    
    def load_baseline(self):
        """加载基线性能数据"""
        if self.baseline_file.exists():
            with open(self.baseline_file) as f:
                self.baseline = json.load(f)
            print(f"Loaded baseline from {self.baseline_file}")
        else:
            print(f"No baseline file found at {self.baseline_file}")
    
    def save_baseline(self):
        """保存当前性能为基线"""
        with open(self.baseline_file, 'w') as f:
            json.dump(self.current, f, indent=2)
        print(f"Saved baseline to {self.baseline_file}")
    
    def run_benchmark(self, name: str, command: List[str]) -> float:
        """运行基准测试命令,返回执行时间(秒)"""
        print(f"Running benchmark: {name}")
        
        start = time.perf_counter()
        result = subprocess.run(command, capture_output=True, text=True)
        end = time.perf_counter()
        
        elapsed = end - start
        
        if result.returncode != 0:
            print(f"Benchmark failed: {result.stderr}")
            return -1
        
        self.current[name] = elapsed
        print(f"  Time: {elapsed:.3f}s")
        
        return elapsed
    
    def compare_with_baseline(self) -> List[str]:
        """与基线对比,返回回归列表"""
        regressions = []
        
        for name, current_time in self.current.items():
            if name not in self.baseline:
                print(f"New benchmark: {name}")
                continue
            
            baseline_time = self.baseline[name]
            change_percent = ((current_time - baseline_time) / baseline_time) * 100
            
            if change_percent > self.threshold_percent:
                regression = f"{name}: {change_percent:+.1f}% (baseline: {baseline_time:.3f}s, current: {current_time:.3f}s)"
                regressions.append(regression)
                print(f"⚠️  Regression detected: {regression}")
            elif change_percent < -self.threshold_percent:
                print(f"✅ Improvement: {name}: {change_percent:+.1f}%")
            else:
                print(f"✅ No significant change: {name}: {change_percent:+.1f}%")
        
        return regressions
    
    def generate_report(self, output_path: str = "performance_report.md"):
        """生成性能回归报告"""
        regressions = self.compare_with_baseline()
        
        with open(output_path, 'w') as f:
            f.write("# Performance Regression Report\n\n")
            f.write(f"Generated: {time.strftime('%Y-%m-%d %H:%M:%S')}\n\n")
            
            if regressions:
                f.write("## ⚠️ Regressions Detected\n\n")
                for r in regressions:
                    f.write(f"- {r}\n")
            else:
                f.write("## ✅ No Regressions Detected\n\n")
            
            f.write("\n## Detailed Comparison\n\n")
            f.write("| Benchmark | Baseline (s) | Current (s) | Change (%) |\n")
            f.write("|-----------|---------------|-------------|-------------|\n")
            
            for name in sorted(set(list(self.baseline.keys()) + list(self.current.keys()))):
                baseline = self.baseline.get(name, float('nan'))
                current = self.current.get(name, float('nan'))
                
                if baseline is None or current is None:
                    change = "N/A"
                else:
                    change = f"{((current - baseline) / baseline) * 100:+.1f}%"
                
                f.write(f"| {name} | {baseline:.3f} | {current:.3f} | {change} |\n")
        
        print(f"\nReport generated: {output_path}")

# CI/CD集成示例(GitHub Actions)
def create_github_actions_workflow():
    """生成GitHub Actions性能回归测试配置"""
    workflow = """
name: Performance Regression Test

on:
  pull_request:
    branches: [main]

jobs:
  performance-test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Setup Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.11'
      
      - name: Install dependencies
        run: pip install -r requirements.txt
      
      - name: Run performance tests
        run: python performance_regression.py --compare
      
      - name: Upload report
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: performance-report
          path: performance_report.md
      
      - name: Comment PR
        if: github.event_name == 'pull_request'
        uses: actions/github-script@v7
        with:
          script: |
            const fs = require('fs');
            const report = fs.readFileSync('performance_report.md', 'utf8');
            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: report
            });
"""
    
    with open('.github/workflows/performance-regression.yml', 'w') as f:
        f.write(workflow)
    
    print("Created GitHub Actions workflow: .github/workflows/performance-regression.yml")

if __name__ == '__main__':
    # 运行性能回归测试
    tester = PerformanceRegressionTest()
    tester.load_baseline()
    
    # 运行基准测试
    tester.run_benchmark("api_response_time", ["python", "test_api.py"])
    tester.run_benchmark("db_query_time", ["python", "test_db.py"])
    tester.run_benchmark("memory_usage", ["python", "test_memory.py"])
    
    # 对比并生成报告
    tester.compare_with_baseline()
    tester.generate_report()
    
    # 更新基线(如果是main分支)
    if os.getenv('GITHUB_REF') == 'refs/heads/main':
        tester.save_baseline()

6. 自动性能瓶颈诊断

# bottleneck_diagnosis.py - MonkeyCode生成
import psutil
import time
import threading
from typing import List, Dict, Any
import asyncio
import aiohttp
import json

class BottleneckDiagnoser:
    """性能瓶颈自动诊断"""
    
    def __init__(self, api_url: str):
        self.api_url = api_url
        self.metrics: Dict[str, List[float]] = {
            "cpu_usage": [],
            "memory_usage": [],
            "response_time": [],
            "active_connections": [],
        }
        self.running = False
    
    def start_monitoring(self, interval: float = 1.0):
        """开始监控系统资源"""
        self.running = True
        
        def monitor():
            while self.running:
                # CPU使用率
                cpu_percent = psutil.cpu_percent(interval=0.1)
                self.metrics["cpu_usage"].append(cpu_percent)
                
                # 内存使用率
                memory = psutil.virtual_memory()
                self.metrics["memory_usage"].append(memory.percent)
                
                time.sleep(interval)
        
        monitor_thread = threading.Thread(target=monitor, daemon=True)
        monitor_thread.start()
        print(f"Started system monitoring (interval: {interval}s)")
    
    def stop_monitoring(self):
        """停止监控"""
        self.running = False
        print("Stopped system monitoring")
    
    async def measure_response_time(self, endpoint: str, requests: int = 100):
        """测量API响应时间"""
        print(f"Measuring response time for {endpoint} ({requests} requests)...")
        
        async with aiohttp.ClientSession() as session:
            tasks = []
            for _ in range(requests):
                task = self._single_request(session, endpoint)
                tasks.append(task)
            
            results = await asyncio.gather(*tasks)
        
        self.metrics["response_time"].extend([r for r in results if r > 0])
        
        avg_time = sum(self.metrics["response_time"]) / len(self.metrics["response_time"])
        print(f"Average response time: {avg_time:.2f} ms")
        
        return avg_time
    
    async def _single_request(self, session: aiohttp.ClientSession, endpoint: str) -> float:
        """发送单个请求"""
        url = f"{self.api_url}{endpoint}"
        
        start = time.perf_counter()
        try:
            async with session.get(url, timeout=aiohttp.ClientTimeout(total=10)) as response:
                await response.text()
                elapsed = (time.perf_counter() - start) * 1000
                return elapsed
        except:
            return -1
    
    def diagnose(self) -> Dict[str, Any]:
        """诊断性能瓶颈"""
        diagnosis = {
            "bottlenecks": [],
            "recommendations": [],
        }
        
        # CPU瓶颈
        if self.metrics["cpu_usage"]:
            avg_cpu = sum(self.metrics["cpu_usage"]) / len(self.metrics["cpu_usage"])
            if avg_cpu > 80:
                diagnosis["bottlenecks"].append(f"High CPU usage: {avg_cpu:.1f}%")
                diagnosis["recommendations"].append("Consider optimizing CPU-intensive operations or scaling horizontally")
        
        # 内存瓶颈
        if self.metrics["memory_usage"]:
            avg_mem = sum(self.metrics["memory_usage"]) / len(self.metrics["memory_usage"])
            if avg_mem > 80:
                diagnosis["bottlenecks"].append(f"High memory usage: {avg_mem:.1f}%")
                diagnosis["recommendations"].append("Check for memory leaks or increase memory limit")
        
        # 响应时间瓶颈
        if self.metrics["response_time"]:
            avg_rt = sum(self.metrics["response_time"]) / len(self.metrics["response_time"])
            if avg_rt > 500:  # 响应时间超过500ms
                diagnosis["bottlenecks"].append(f"Slow API response: {avg_rt:.1f} ms")
                diagnosis["recommendations"].append("Optimize database queries, add caching, or use async processing")
        
        # 如果没有发现瓶颈
        if not diagnosis["bottlenecks"]:
            diagnosis["bottlenecks"].append("No significant bottlenecks detected")
        
        return diagnosis
    
    def generate_diagnosis_report(self, output_path: str = "bottleneck_diagnosis.json"):
        """生成诊断报告"""
        diagnosis = self.diagnose()
        
        report = {
            "timestamp": time.strftime("%Y-%m-%d %H:%M:%S"),
            "metrics_summary": {
                "cpu_usage_avg": sum(self.metrics["cpu_usage"]) / len(self.metrics["cpu_usage"]) if self.metrics["cpu_usage"] else 0,
                "memory_usage_avg": sum(self.metrics["memory_usage"]) / len(self.metrics["memory_usage"]) if self.metrics["memory_usage"] else 0,
                "response_time_avg": sum(self.metrics["response_time"]) / len(self.metrics["response_time"]) if self.metrics["response_time"] else 0,
            },
            "diagnosis": diagnosis,
        }
        
        with open(output_path, 'w') as f:
            json.dump(report, f, indent=2)
        
        print(f"\n{'='*60}")
        print(f"Bottleneck Diagnosis Report")
        print(f"{'='*60}")
        print(f"CPU Usage (avg): {report['metrics_summary']['cpu_usage_avg']:.1f}%")
        print(f"Memory Usage (avg): {report['metrics_summary']['memory_usage_avg']:.1f}%")
        print(f"Response Time (avg): {report['metrics_summary']['response_time_avg']:.1f} ms")
        
        print(f"\nBottlenecks:")
        for bottleneck in diagnosis["bottlenecks"]:
            print(f"  ⚠️  {bottleneck}")
        
        print(f"\nRecommendations:")
        for rec in diagnosis["recommendations"]:
            print(f"  💡 {rec}")
        
        print(f"\nReport saved to {output_path}")

if __name__ == '__main__':
    # 诊断API性能瓶颈
    diagnoser = BottleneckDiagnoser(api_url="http://localhost:8000")
    
    # 开始监控
    diagnoser.start_monitoring(interval=0.5)
    
    # 运行负载测试
    asyncio.run(diagnoser.measure_response_time(endpoint="/api/data", requests=500))
    
    # 停止监控
    diagnoser.stop_monitoring()
    
    # 生成诊断报告
    diagnoser.generate_diagnosis_report()

7. 完整性能优化工作流

# performance_optimization_workflow.py - MonkeyCode生成
"""
完整的性能优化工作流:
1. 基准测试(建立基线)
2. Profiling(定位瓶颈)
3. 优化(代码修改)
4. 回归测试(验证优化效果)
5. 部署(灰度发布)
"""

import json
import time
from typing import Callable, Any
import functools

class PerformanceOptimizationWorkflow:
    """性能优化工作流"""
    
    def __init__(self, baseline_file: str = "performance_baseline.json"):
        self.baseline_file = baseline_file
        self.baseline = {}
        self.load_baseline()
    
    def load_baseline(self):
        """加载基线数据"""
        try:
            with open(self.baseline_file) as f:
                self.baseline = json.load(f)
        except FileNotFoundError:
            print("No baseline found. Run baseline tests first.")
    
    def save_baseline(self):
        """保存基线数据"""
        with open(self.baseline_file, 'w') as f:
            json.dump(self.baseline, f, indent=2)
        print(f"Baseline saved to {self.baseline_file}")
    
    def establish_baseline(self, func: Callable, *args, **kwargs):
        """建立性能基线"""
        print(f"Establishing baseline for {func.__name__}")
        
        # 运行多次取平均
        times = []
        for _ in range(100):
            start = time.perf_counter()
            result = func(*args, **kwargs)
            end = time.perf_counter()
            times.append((end - start) * 1000)
        
        avg_time = sum(times) / len(times)
        
        self.baseline[func.__name__] = {
            "avg_time_ms": avg_time,
            "min_ms": min(times),
            "max_ms": max(times),
            "timestamp": time.strftime("%Y-%m-%d %H:%M:%S"),
        }
        
        print(f"Baseline established: {avg_time:.3f} ms")
        self.save_baseline()
        
        return result
    
    def compare_with_baseline(self, func: Callable, *args, **kwargs):
        """与基线对比"""
        if func.__name__ not in self.baseline:
            print(f"No baseline found for {func.__name__}. Establishing baseline first.")
            return self.establish_baseline(func, *args, **kwargs)
        
        baseline_data = self.baseline[func.__name__]
        baseline_time = baseline_data["avg_time_ms"]
        
        # 测量当前性能
        times = []
        for _ in range(100):
            start = time.perf_counter()
            result = func(*args, **kwargs)
            end = time.perf_counter()
            times.append((end - start) * 1000)
        
        current_time = sum(times) / len(times)
        
        # 对比
        change_percent = ((current_time - baseline_time) / baseline_time) * 100
        
        print(f"\n{'='*60}")
        print(f"Performance Comparison: {func.__name__}")
        print(f"{'='*60}")
        print(f"Baseline: {baseline_time:.3f} ms")
        print(f"Current: {current_time:.3f} ms")
        print(f"Change: {change_percent:+.1f}%")
        
        if change_percent > 5:
            print(f"⚠️  Performance regression detected!")
        elif change_percent < -5:
            print(f"✅ Performance improvement: {-change_percent:.1f}% faster")
        else:
            print(f"✅ No significant change")
        
        return result
    
    def optimize_and_validate(self, original_func: Callable, optimized_func: Callable, *args, **kwargs):
        """优化并验证"""
        print(f"\n{'='*60}")
        print(f"Optimization Validation")
        print(f"{'='*60}")
        
        # 测试原始函数
        print(f"\n1. Testing original function: {original_func.__name__}")
        original_result = self.compare_with_baseline(original_func, *args, **kwargs)
        
        # 测试优化后的函数
        print(f"\n2. Testing optimized function: {optimized_func.__name__}")
        optimized_result = self.compare_with_baseline(optimized_func, *args, **kwargs)
        
        # 验证结果一致性
        if original_result == optimized_result:
            print(f"\n✅ Results match! Optimization is safe.")
        else:
            print(f"\n⚠️  Results don't match! Check optimization.")
            print(f"Original: {original_result}")
            print(f"Optimized: {optimized_result}")
        
        return optimized_result

# 使用示例
def original_slow_function(data: list) -> list:
    """原始慢函数"""
    result = []
    for item in data:
        if item % 2 == 0:
            result.append(item ** 2)
    return result

def optimized_fast_function(data: list) -> list:
    """优化后的快函数"""
    return [item ** 2 for item in data if item % 2 == 0]

if __name__ == '__main__':
    workflow = PerformanceOptimizationWorkflow()
    
    test_data = list(range(10000))
    
    # 建立基线
    workflow.establish_baseline(original_slow_function, test_data)
    
    # 对比优化效果
    workflow.optimize_and_validate(
        original_slow_function,
        optimized_fast_function,
        test_data
    )

MonkeyCode生成的性能测试工具链覆盖了从基准测试到自动优化的完整流程。让AI写功能代码只是第一步,让AI帮你做性能测试才是完整的工作流。但性能优化需要理解业务场景,AI生成的Profiling代码是工具,瓶颈诊断还需要人工经验。

posted @ 2026-05-29 17:08  机房管理员  阅读(14)  评论(0)    收藏  举报