update at 2025-10-30 16:31:47

This commit is contained in:
douboer
2025-10-30 16:31:47 +08:00
parent b431199a00
commit eba2f07990
5 changed files with 985 additions and 1 deletions

325
BENCHMARK_GUIDE.md Normal file
View File

@@ -0,0 +1,325 @@
# 并发性能测试使用指南
## 📋 概述
本项目提供了完整的并发性能测试工具,用于评估 YOLO 数字识别系统在多用户同时访问场景下的性能表现。
## 🎯 测试工具
### 1. `benchmark_concurrent.py` - 核心测试脚本
模拟 n 个用户并发执行数字识别任务。
**主要功能:**
- ✅ 支持自定义并发用户数和每用户图片数
- ✅ 循环使用 valid 文件夹中的图片
- ✅ 统计详细的性能指标QPS、响应时间、成功率等
- ✅ 生成文本和 JSON 格式的报告
- ✅ 支持不同的模型和配置参数
**基本用法:**
```bash
# 模拟10个用户每个用户识别20张图片
python scripts/benchmark_concurrent.py --users 10 --images-per-user 20
# 使用详细输出模式
python scripts/benchmark_concurrent.py --users 5 --images-per-user 10 --verbose
# 调整置信度阈值
python scripts/benchmark_concurrent.py --users 10 --images-per-user 20 --conf 0.15
# 指定输出文件
python scripts/benchmark_concurrent.py --users 20 --output results/my_test.txt
```
**完整参数:**
```bash
--users N # 并发用户数量(默认: 10
--images-per-user N # 每个用户识别的图片数量(默认: 20
--model PATH # 模型路径(默认: 最佳模型)
--source DIR # 图片源文件夹(默认: valid
--conf FLOAT # 置信度阈值(默认: 0.2
--imgsz INT # 输入图片大小(默认: 320
--output FILE # 输出报告路径(默认: results/benchmark_report.txt
--verbose # 显示详细日志
```
### 2. `run_benchmark_quick.sh` - 快速测试套件
运行一组预定义的快速测试1, 3, 5, 8 个并发用户),适合快速验证。
**使用方法:**
```bash
./scripts/run_benchmark_quick.sh
```
**测试配置:**
- 并发级别1, 3, 5, 8 用户
- 每用户图片数10 张
- 总共测试10 + 30 + 50 + 80 = 170 张图片
**输出:**
- 各并发级别的详细报告
- 性能对比摘要表格
- 保存位置:`results/benchmark_quick/`
### 3. `run_benchmark_suite.sh` - 完整测试套件
运行更全面的测试1, 3, 5, 10, 15, 20 个并发用户)。
**使用方法:**
```bash
./scripts/run_benchmark_suite.sh
```
**测试配置:**
- 并发级别1, 3, 5, 10, 15, 20 用户
- 每用户图片数20 张
- 总共测试20 + 60 + 100 + 200 + 300 + 400 = 1080 张图片
**输出:**
- 各并发级别的详细报告
- 性能对比摘要表格
- 保存位置:`results/benchmark_suite/`
## 📊 性能指标说明
### 报告包含的指标
**总体性能:**
- **总执行时间**:所有并发用户完成的总时间
- **总识别图片数**:所有用户处理的图片总数
- **成功率**:成功识别的图片比例
- **吞吐量 (QPS)**:每秒处理的图片数量
**响应时间统计:**
- **平均响应时间**:单张图片的平均识别时间
- **最小/最大响应时间**:响应时间的范围
- **P50/P90/P95/P99**:百分位数,表示大部分请求的响应时间
**各用户性能:**
- 每个用户的图片数、成功数、总耗时、平均耗时
## 📈 测试结果示例
### 快速测试结果(实际测试数据)
```
并发用户 | 总图片 | 总耗时(s) | QPS | 平均响应(s)
---------|---------|-----------|--------|------------
1 | 10 | 0.17 | 60.30 | 0.014
3 | 30 | 0.36 | 84.35 | 0.027
5 | 50 | 0.58 | 86.05 | 0.042
8 | 80 | 1.10 | 72.65 | 0.081
```
### 标准测试结果10用户×20图
```
总体性能:
- 总执行时间: 2.85 秒
- 总识别图片数: 200
- 成功: 200 (100.0%)
- 吞吐量 (QPS): 70.28 图片/秒
响应时间统计:
- 平均响应时间: 0.123 秒
- P50 响应时间: 0.109 秒
- P90 响应时间: 0.167 秒
- P99 响应时间: 0.342 秒
```
## 💡 使用建议
### 场景选择
| 测试场景 | 推荐工具 | 适用情况 |
|---------|---------|---------|
| 快速验证 | `run_benchmark_quick.sh` | 代码修改后快速验证性能 |
| 完整评估 | `run_benchmark_suite.sh` | 系统上线前全面测试 |
| 自定义测试 | `benchmark_concurrent.py` | 特定并发场景测试 |
| 压力测试 | `benchmark_concurrent.py` | 使用更高的并发数如50+ |
### 测试流程建议
**1. 开发阶段**
```bash
# 快速测试,验证功能正常
python scripts/benchmark_concurrent.py --users 3 --images-per-user 5 --verbose
```
**2. 性能优化**
```bash
# 运行快速套件,对比优化前后
./scripts/run_benchmark_quick.sh
```
**3. 上线前验证**
```bash
# 运行完整套件,全面评估
./scripts/run_benchmark_suite.sh
```
**4. 压力测试**
```bash
# 高并发测试
python scripts/benchmark_concurrent.py --users 50 --images-per-user 10
```
### 性能调优建议
如果性能不理想,可以尝试:
1. **调整模型参数**
```bash
# 降低置信度阈值
--conf 0.15
# 减小输入图片尺寸
--imgsz 256
```
2. **优化并发配置**
- 根据 CPU 核心数调整并发用户数
- 监控系统资源使用情况
3. **使用更快的模型**
```bash
# 使用更小的模型(如果准确率满足要求)
--model yolov8n.pt
```
## 📁 输出文件说明
### 文本报告 (`.txt`)
详细的性能测试报告,包含:
- 测试配置信息
- 总体性能统计
- 响应时间分析
- 各用户执行情况
- 失败任务详情(如有)
### JSON 数据 (`.json`)
结构化的测试数据,包含:
```json
{
"config": {...}, // 测试配置
"summary": { // 总体统计
"total_time": 2.85,
"total_images": 200,
"qps": 70.28
},
"users": [ // 每个用户的详细结果
{
"user_id": 0,
"results": [...], // 每张图片的识别结果
"total_time": 2.50,
"success_count": 20,
"avg_time": 0.125
}
]
}
```
JSON 文件可用于:
- 后续数据分析
- 生成自定义报告
- 性能趋势对比
## 🔍 故障排查
### 问题:测试时间过长
**原因:** 并发数或图片数设置过高
**解决:**
```bash
# 减少并发数或图片数
python scripts/benchmark_concurrent.py --users 5 --images-per-user 10
```
### 问题:部分识别失败
**原因:** 图片质量差或置信度阈值过高
**解决:**
```bash
# 降低置信度阈值
python scripts/benchmark_concurrent.py --conf 0.1
# 或先对图片进行预处理
python scripts/preprocess_images.py --input valid --output valid-processed --method clahe
python scripts/benchmark_concurrent.py --source valid-processed
```
### 问题QPS 过低
**可能原因:**
- 模型加载开销(每个线程独立加载)
- 图片尺寸过大
- 系统资源不足
**优化建议:**
- 减小 `--imgsz` 参数
- 关闭其他占用资源的程序
- 监控 CPU 使用率
## 📝 自定义测试
### 创建自定义测试脚本
```bash
#!/bin/bash
# 自定义测试场景
# 场景1低并发长时间测试
python scripts/benchmark_concurrent.py \
--users 2 \
--images-per-user 100 \
--output results/scenario1.txt
# 场景2高并发短时间测试
python scripts/benchmark_concurrent.py \
--users 50 \
--images-per-user 5 \
--output results/scenario2.txt
# 场景3不同配置对比
for conf in 0.1 0.15 0.2 0.25; do
python scripts/benchmark_concurrent.py \
--users 10 \
--images-per-user 10 \
--conf $conf \
--output results/benchmark_conf_${conf}.txt
done
```
## 🎓 最佳实践
1. **基线测试**:先建立性能基线(单用户测试)
2. **逐步加压**:从低并发逐步提高到高并发
3. **多次测试**每个场景测试3-5次取平均值
4. **记录环境**:记录测试时的系统环境和资源使用情况
5. **对比分析**:保存历史测试数据,进行趋势分析
## 📞 相关文档
- [README.md](../README.md) - 项目完整文档
- [QUICKSTART.md](../QUICKSTART.md) - 快速开始指南
- [FINAL_REPORT.md](../FINAL_REPORT.md) - 项目性能报告
---
**作者**: Gavin Chan
**创建日期**: 2025-01-30
**最后更新**: 2025-01-30

View File

@@ -8,6 +8,7 @@
-**完整流程**: 从数据预处理到模型训练再到批量识别的完整pipeline -**完整流程**: 从数据预处理到模型训练再到批量识别的完整pipeline
-**易于使用**: 提供交互式脚本和一键运行工具 -**易于使用**: 提供交互式脚本和一键运行工具
-**可视化结果**: 自动生成带标注的可视化图片 -**可视化结果**: 自动生成带标注的可视化图片
-**性能测试**: 内置并发性能测试工具,评估多用户场景性能
## 📁 项目结构 ## 📁 项目结构
@@ -388,13 +389,50 @@ python scripts/predict_digits_improved.py --source valid-processed --conf 0.15
4. **超参数调优**: 调整学习率、优化器等参数 4. **超参数调优**: 调整学习率、优化器等参数
5. **后处理优化**: 根据业务规则必须4位数字进行后处理 5. **后处理优化**: 根据业务规则必须4位数字进行后处理
## <EFBFBD> 项目文档 ## 🧪 性能测试
### 并发性能测试
模拟多用户同时访问场景,评估系统性能:
```bash
# 快速测试1, 3, 5, 8 个并发用户)
./scripts/run_benchmark_quick.sh
# 标准测试(自定义并发数)
python scripts/benchmark_concurrent.py --users 10 --images-per-user 20
# 详细输出模式
python scripts/benchmark_concurrent.py --users 5 --images-per-user 10 --verbose
```
### 测试结果示例
实际测试数据Apple M2CPU模式
| 并发用户 | 总图片 | 总耗时(s) | QPS | 平均响应(s) |
|---------|-------|----------|-----|------------|
| 1 | 10 | 0.17 | 60.30 | 0.014 |
| 3 | 30 | 0.36 | 84.35 | 0.027 |
| 5 | 50 | 0.58 | 86.05 | 0.042 |
| 8 | 80 | 1.10 | 72.65 | 0.081 |
| 10 | 200 | 2.85 | 70.28 | 0.123 |
**性能指标说明:**
- **QPS**: 每秒处理的图片数量(越高越好)
- **平均响应时间**: 单张图片识别耗时(越低越好)
- **最佳并发数**: 3-5 个用户时 QPS 最高
详细使用方法请参考 [BENCHMARK_GUIDE.md](BENCHMARK_GUIDE.md)
## 📂 项目文档
- **README.md**: 完整使用文档(本文件) - **README.md**: 完整使用文档(本文件)
- **QUICKSTART.md**: 5分钟快速上手指南 - **QUICKSTART.md**: 5分钟快速上手指南
- **FINAL_REPORT.md**: 项目完成报告和性能分析 - **FINAL_REPORT.md**: 项目完成报告和性能分析
- **PROJECT_STRUCTURE.md**: 详细的项目结构说明 - **PROJECT_STRUCTURE.md**: 详细的项目结构说明
- **CODE_CLEANUP_DONE.md**: 代码清理和优化记录 - **CODE_CLEANUP_DONE.md**: 代码清理和优化记录
- **BENCHMARK_GUIDE.md**: 并发性能测试详细指南
## 👨‍💻 作者 ## 👨‍💻 作者

446
scripts/benchmark_concurrent.py Executable file
View File

@@ -0,0 +1,446 @@
#!/usr/bin/env python3
"""
并发性能测试脚本 - 模拟多用户同时识别
功能:
- 模拟 n 个用户并发执行数字识别任务
- 循环使用 valid 文件夹中的图片
- 统计总体性能指标(吞吐量、响应时间、成功率)
- 生成详细的性能报告
使用方法:
# 模拟10个并发用户每个用户识别20张图片
python scripts/benchmark_concurrent.py --users 10 --images-per-user 20
# 使用不同的模型和配置
python scripts/benchmark_concurrent.py --users 5 --images-per-user 10 --conf 0.15
# 指定输出报告文件
python scripts/benchmark_concurrent.py --users 20 --output results/benchmark_report.txt
输出:
- 实时进度显示
- 详细的性能统计总时间、平均响应时间、QPS等
- 每个用户的执行情况
- 失败任务的详细信息
作者: Gavin Chan
日期: 2025-01-30
"""
import argparse
import time
import os
from pathlib import Path
from concurrent.futures import ThreadPoolExecutor, as_completed
from ultralytics import YOLO
import cv2
import numpy as np
from datetime import datetime
import json
def parse_args():
"""解析命令行参数"""
parser = argparse.ArgumentParser(description='并发性能测试 - 模拟多用户识别')
parser.add_argument('--users', type=int, default=10,
help='并发用户数量(默认: 10')
parser.add_argument('--images-per-user', type=int, default=20,
help='每个用户识别的图片数量(默认: 20')
parser.add_argument('--model', type=str,
default='runs/digit_yolo/exp_preprocessed_color_150/weights/best.pt',
help='模型路径')
parser.add_argument('--source', type=str, default='valid',
help='图片源文件夹(默认: valid')
parser.add_argument('--conf', type=float, default=0.2,
help='置信度阈值(默认: 0.2')
parser.add_argument('--imgsz', type=int, default=320,
help='输入图片大小(默认: 320')
parser.add_argument('--output', type=str, default='results/benchmark_report.txt',
help='输出报告文件路径')
parser.add_argument('--verbose', action='store_true',
help='显示详细日志')
return parser.parse_args()
def load_image_paths(source_dir):
"""
加载所有图片路径
Args:
source_dir: 图片文件夹路径
Returns:
list: 图片路径列表
"""
source_path = Path(source_dir)
if not source_path.exists():
raise FileNotFoundError(f"图片文件夹不存在: {source_dir}")
# 支持的图片格式
extensions = ('.jpg', '.jpeg', '.png', '.bmp', '.webp')
image_paths = [str(p) for p in source_path.iterdir()
if p.suffix.lower() in extensions]
if not image_paths:
raise ValueError(f"文件夹中没有找到图片: {source_dir}")
return sorted(image_paths)
def extract_digits_from_predictions(results):
"""
从YOLO预测结果中提取数字
Args:
results: YOLO预测结果对象
Returns:
tuple: (识别的数字字符串, 平均置信度, 检测到的数字个数)
"""
if not results or len(results) == 0:
return "", 0.0, 0
result = results[0]
if result.boxes is None or len(result.boxes) == 0:
return "", 0.0, 0
# 获取所有检测框
boxes = result.boxes.cpu().numpy()
# 提取类别、置信度和位置
detections = []
for box in boxes:
cls = int(box.cls[0])
conf = float(box.conf[0])
x_center = float((box.xyxy[0][0] + box.xyxy[0][2]) / 2)
detections.append((cls, conf, x_center))
if not detections:
return "", 0.0, 0
# 按x坐标排序从左到右
detections.sort(key=lambda x: x[2])
# 提取数字和置信度
digits = [str(d[0]) for d in detections]
confidences = [d[1] for d in detections]
result_str = ''.join(digits)
avg_conf = sum(confidences) / len(confidences)
return result_str, avg_conf, len(detections)
def recognize_single_image(model, image_path, conf_threshold, img_size):
"""
识别单张图片
Args:
model: YOLO模型对象
image_path: 图片路径
conf_threshold: 置信度阈值
img_size: 输入图片大小
Returns:
dict: 识别结果 {
'filename': 文件名,
'digits': 识别的数字,
'confidence': 置信度,
'count': 数字个数,
'time': 耗时(秒),
'success': 是否成功
}
"""
start_time = time.time()
try:
# 执行预测
results = model.predict(
source=image_path,
conf=conf_threshold,
imgsz=img_size,
verbose=False
)
# 提取数字
digits, confidence, count = extract_digits_from_predictions(results)
elapsed = time.time() - start_time
return {
'filename': Path(image_path).name,
'digits': digits,
'confidence': confidence,
'count': count,
'time': elapsed,
'success': True,
'error': None
}
except Exception as e:
elapsed = time.time() - start_time
return {
'filename': Path(image_path).name,
'digits': '',
'confidence': 0.0,
'count': 0,
'time': elapsed,
'success': False,
'error': str(e)
}
def user_task(user_id, model_path, image_paths, num_images, conf_threshold, img_size, verbose):
"""
单个用户的识别任务
Args:
user_id: 用户ID
model_path: 模型路径
image_paths: 所有可用图片路径
num_images: 该用户要识别的图片数量
conf_threshold: 置信度阈值
img_size: 图片大小
verbose: 是否显示详细日志
Returns:
dict: 用户任务结果 {
'user_id': 用户ID,
'results': 识别结果列表,
'total_time': 总耗时,
'success_count': 成功数量,
'avg_time': 平均耗时
}
"""
# 加载模型(每个线程独立加载)
model = YOLO(model_path)
# 循环使用图片
user_images = []
for i in range(num_images):
img_idx = (user_id * num_images + i) % len(image_paths)
user_images.append(image_paths[img_idx])
start_time = time.time()
results = []
for img_path in user_images:
result = recognize_single_image(model, img_path, conf_threshold, img_size)
results.append(result)
if verbose:
status = "" if result['success'] else ""
print(f" [{status}] 用户{user_id:2d} | {result['filename']:20s} | "
f"{result['digits']:6s} | {result['time']:.3f}s")
total_time = time.time() - start_time
success_count = sum(1 for r in results if r['success'])
avg_time = sum(r['time'] for r in results) / len(results) if results else 0
return {
'user_id': user_id,
'results': results,
'total_time': total_time,
'success_count': success_count,
'avg_time': avg_time
}
def generate_report(user_results, total_time, args):
"""
生成性能测试报告
Args:
user_results: 所有用户的结果列表
total_time: 总执行时间
args: 命令行参数
Returns:
str: 报告内容
"""
# 统计数据
total_images = sum(len(ur['results']) for ur in user_results)
total_success = sum(ur['success_count'] for ur in user_results)
total_failed = total_images - total_success
success_rate = (total_success / total_images * 100) if total_images > 0 else 0
# 响应时间统计
all_times = [r['time'] for ur in user_results for r in ur['results']]
avg_response_time = sum(all_times) / len(all_times) if all_times else 0
min_response_time = min(all_times) if all_times else 0
max_response_time = max(all_times) if all_times else 0
# 吞吐量
qps = total_images / total_time if total_time > 0 else 0
# 生成报告
report = []
report.append("=" * 80)
report.append("YOLO 数字识别并发性能测试报告")
report.append("=" * 80)
report.append(f"\n测试时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
report.append(f"\n配置参数:")
report.append(f" - 并发用户数: {args.users}")
report.append(f" - 每用户图片数: {args.images_per_user}")
report.append(f" - 模型: {args.model}")
report.append(f" - 置信度阈值: {args.conf}")
report.append(f" - 图片大小: {args.imgsz}")
report.append(f" - 图片源: {args.source}")
report.append(f"\n总体性能:")
report.append(f" - 总执行时间: {total_time:.2f}")
report.append(f" - 总识别图片数: {total_images}")
report.append(f" - 成功: {total_success} ({success_rate:.1f}%)")
report.append(f" - 失败: {total_failed}")
report.append(f" - 吞吐量 (QPS): {qps:.2f} 图片/秒")
report.append(f"\n响应时间统计:")
report.append(f" - 平均响应时间: {avg_response_time:.3f}")
report.append(f" - 最小响应时间: {min_response_time:.3f}")
report.append(f" - 最大响应时间: {max_response_time:.3f}")
# 百分位数
sorted_times = sorted(all_times)
p50_idx = int(len(sorted_times) * 0.50)
p90_idx = int(len(sorted_times) * 0.90)
p95_idx = int(len(sorted_times) * 0.95)
p99_idx = int(len(sorted_times) * 0.99)
report.append(f" - P50 响应时间: {sorted_times[p50_idx]:.3f}")
report.append(f" - P90 响应时间: {sorted_times[p90_idx]:.3f}")
report.append(f" - P95 响应时间: {sorted_times[p95_idx]:.3f}")
report.append(f" - P99 响应时间: {sorted_times[p99_idx]:.3f}")
# 每个用户的统计
report.append(f"\n各用户性能:")
report.append(f" {'用户ID':>8} | {'图片数':>8} | {'成功':>8} | {'总耗时':>10} | {'平均耗时':>10}")
report.append(f" {'-'*8}-+-{'-'*8}-+-{'-'*8}-+-{'-'*10}-+-{'-'*10}")
for ur in sorted(user_results, key=lambda x: x['user_id']):
report.append(f" {ur['user_id']:8d} | {len(ur['results']):8d} | "
f"{ur['success_count']:8d} | {ur['total_time']:9.2f}s | "
f"{ur['avg_time']:9.3f}s")
# 失败任务详情
failed_tasks = []
for ur in user_results:
for r in ur['results']:
if not r['success']:
failed_tasks.append((ur['user_id'], r))
if failed_tasks:
report.append(f"\n失败任务详情 (共 {len(failed_tasks)} 个):")
for user_id, result in failed_tasks[:20]: # 最多显示20个
report.append(f" 用户{user_id} | {result['filename']} | 错误: {result['error']}")
if len(failed_tasks) > 20:
report.append(f" ... 还有 {len(failed_tasks) - 20} 个失败任务")
report.append("\n" + "=" * 80)
return '\n'.join(report)
def main():
"""主函数"""
args = parse_args()
# 检查模型文件
if not Path(args.model).exists():
print(f"❌ 模型文件不存在: {args.model}")
return
# 加载图片路径
print(f"\n📂 加载图片路径...")
try:
image_paths = load_image_paths(args.source)
print(f"✓ 找到 {len(image_paths)} 张图片")
except Exception as e:
print(f"❌ 加载图片失败: {e}")
return
# 显示测试配置
total_images = args.users * args.images_per_user
print(f"\n⚙️ 测试配置:")
print(f" - 并发用户数: {args.users}")
print(f" - 每用户图片数: {args.images_per_user}")
print(f" - 总图片数: {total_images}")
print(f" - 模型: {Path(args.model).name}")
print(f" - 置信度: {args.conf}")
print(f" - 图片大小: {args.imgsz}")
# 开始测试
print(f"\n🚀 开始并发测试...\n")
start_time = time.time()
# 使用线程池并发执行
user_results = []
with ThreadPoolExecutor(max_workers=args.users) as executor:
# 提交所有用户任务
futures = []
for user_id in range(args.users):
future = executor.submit(
user_task,
user_id,
args.model,
image_paths,
args.images_per_user,
args.conf,
args.imgsz,
args.verbose
)
futures.append(future)
# 收集结果并显示进度
completed = 0
for future in as_completed(futures):
result = future.result()
user_results.append(result)
completed += 1
if not args.verbose:
print(f" 进度: {completed}/{args.users} 用户完成 "
f"({completed/args.users*100:.0f}%)", end='\r')
total_time = time.time() - start_time
if not args.verbose:
print() # 换行
# 生成报告
print(f"\n📊 生成性能报告...")
report = generate_report(user_results, total_time, args)
# 显示报告
print(report)
# 保存报告
output_path = Path(args.output)
output_path.parent.mkdir(parents=True, exist_ok=True)
with open(output_path, 'w', encoding='utf-8') as f:
f.write(report)
print(f"\n✓ 报告已保存到: {args.output}")
# 保存JSON格式的详细数据
json_output = output_path.with_suffix('.json')
json_data = {
'config': vars(args),
'summary': {
'total_time': total_time,
'total_images': total_images,
'qps': total_images / total_time if total_time > 0 else 0,
},
'users': user_results
}
with open(json_output, 'w', encoding='utf-8') as f:
json.dump(json_data, f, indent=2, ensure_ascii=False)
print(f"✓ 详细数据已保存到: {json_output}")
if __name__ == '__main__':
main()

71
scripts/run_benchmark_quick.sh Executable file
View File

@@ -0,0 +1,71 @@
#!/bin/bash
# 快速并发性能测试(较小规模,适合快速验证)
echo "================================================================================"
echo "YOLO 数字识别 - 快速并发测试"
echo "================================================================================"
echo ""
RESULT_DIR="results/benchmark_quick"
mkdir -p "$RESULT_DIR"
IMAGES_PER_USER=10
USER_COUNTS=(1 3 5 8)
echo "📋 测试配置:"
echo " - 每用户图片数: $IMAGES_PER_USER"
echo " - 测试并发级别: ${USER_COUNTS[@]}"
echo ""
for users in "${USER_COUNTS[@]}"; do
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "🚀 测试: ${users} 个并发用户 × ${IMAGES_PER_USER} 张图片"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
python scripts/benchmark_concurrent.py \
--users "$users" \
--images-per-user "$IMAGES_PER_USER" \
--output "$RESULT_DIR/benchmark_${users}users.txt"
echo ""
done
# 生成摘要
SUMMARY_FILE="$RESULT_DIR/summary.txt"
{
echo "================================================================================"
echo "YOLO 数字识别 - 快速并发测试摘要"
echo "================================================================================"
echo ""
echo "测试时间: $(date '+%Y-%m-%d %H:%M:%S')"
echo ""
echo "性能对比:"
echo ""
printf "%-12s | %-10s | %-12s | %-12s | %-15s\n" \
"并发用户" "总图片" "总耗时(s)" "QPS" "平均响应(s)"
echo "-------------+------------+--------------+--------------+----------------"
} > "$SUMMARY_FILE"
for users in "${USER_COUNTS[@]}"; do
json_file="$RESULT_DIR/benchmark_${users}users.json"
if [ -f "$json_file" ]; then
total_images=$(python3 -c "import json; print(json.load(open('$json_file'))['summary']['total_images'])")
total_time=$(python3 -c "import json; print(f\"{json.load(open('$json_file'))['summary']['total_time']:.2f}\")")
qps=$(python3 -c "import json; print(f\"{json.load(open('$json_file'))['summary']['qps']:.2f}\")")
avg_time=$(python3 -c "import json; d=json.load(open('$json_file')); times=[r['time'] for u in d['users'] for r in u['results']]; print(f\"{sum(times)/len(times):.3f}\")")
printf "%-12d | %-10d | %-12s | %-12s | %-15s\n" \
"$users" "$total_images" "$total_time" "$qps" "$avg_time" >> "$SUMMARY_FILE"
fi
done
echo "" >> "$SUMMARY_FILE"
echo "================================================================================" >> "$SUMMARY_FILE"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
cat "$SUMMARY_FILE"
echo ""
echo "✅ 快速测试完成!详细报告: $RESULT_DIR/"
echo "================================================================================"

104
scripts/run_benchmark_suite.sh Executable file
View File

@@ -0,0 +1,104 @@
#!/bin/bash
#
# 并发性能测试套件 - 运行多组测试对比不同并发级别的性能
#
# 功能:
# - 测试不同并发用户数下的系统性能
# - 生成对比报告
#
# 使用:
# ./scripts/run_benchmark_suite.sh
#
# 作者: Gavin Chan
# 日期: 2025-01-30
echo "================================================================================"
echo "YOLO 数字识别 - 并发性能测试套件"
echo "================================================================================"
echo ""
# 创建结果目录
RESULT_DIR="results/benchmark_suite"
mkdir -p "$RESULT_DIR"
# 测试配置
IMAGES_PER_USER=20
USER_COUNTS=(1 3 5 10 15 20)
echo "📋 测试配置:"
echo " - 每用户图片数: $IMAGES_PER_USER"
echo " - 测试并发级别: ${USER_COUNTS[@]}"
echo ""
# 运行测试
for users in "${USER_COUNTS[@]}"; do
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "🚀 测试场景: ${users} 个并发用户"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
output_file="$RESULT_DIR/benchmark_${users}users.txt"
python scripts/benchmark_concurrent.py \
--users "$users" \
--images-per-user "$IMAGES_PER_USER" \
--output "$output_file"
echo ""
sleep 1 # 短暂休息,避免系统过载
done
# 生成对比报告
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "📊 生成对比报告..."
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
SUMMARY_FILE="$RESULT_DIR/summary.txt"
cat > "$SUMMARY_FILE" << 'EOF'
================================================================================
YOLO 数字识别 - 并发性能对比报告
================================================================================
EOF
echo "测试时间: $(date '+%Y-%m-%d %H:%M:%S')" >> "$SUMMARY_FILE"
echo "" >> "$SUMMARY_FILE"
echo "各并发级别性能对比:" >> "$SUMMARY_FILE"
echo "" >> "$SUMMARY_FILE"
printf "%-12s | %-10s | %-12s | %-12s | %-15s\n" \
"并发用户" "总图片" "总耗时(s)" "QPS" "平均响应(s)" >> "$SUMMARY_FILE"
echo "-------------+------------+--------------+--------------+----------------" >> "$SUMMARY_FILE"
# 从JSON文件中提取数据
for users in "${USER_COUNTS[@]}"; do
json_file="$RESULT_DIR/benchmark_${users}users.json"
if [ -f "$json_file" ]; then
total_images=$(python3 -c "import json; print(json.load(open('$json_file'))['summary']['total_images'])")
total_time=$(python3 -c "import json; print(f\"{json.load(open('$json_file'))['summary']['total_time']:.2f}\")")
qps=$(python3 -c "import json; print(f\"{json.load(open('$json_file'))['summary']['qps']:.2f}\")")
# 计算平均响应时间
avg_time=$(python3 -c "import json; d=json.load(open('$json_file')); times=[r['time'] for u in d['users'] for r in u['results']]; print(f\"{sum(times)/len(times):.3f}\")")
printf "%-12d | %-10d | %-12s | %-12s | %-15s\n" \
"$users" "$total_images" "$total_time" "$qps" "$avg_time" >> "$SUMMARY_FILE"
fi
done
echo "" >> "$SUMMARY_FILE"
echo "================================================================================" >> "$SUMMARY_FILE"
# 显示对比报告
cat "$SUMMARY_FILE"
echo ""
echo "✅ 测试完成!"
echo ""
echo "📁 详细报告位置:"
echo " - 对比摘要: $SUMMARY_FILE"
echo " - 各测试详情: $RESULT_DIR/benchmark_*users.txt"
echo " - JSON数据: $RESULT_DIR/benchmark_*users.json"
echo ""
echo "================================================================================"