update at 2025-10-30 16:31:47
This commit is contained in:
325
BENCHMARK_GUIDE.md
Normal file
325
BENCHMARK_GUIDE.md
Normal 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
|
||||||
40
README.md
40
README.md
@@ -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 M2,CPU模式):
|
||||||
|
|
||||||
|
| 并发用户 | 总图片 | 总耗时(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
446
scripts/benchmark_concurrent.py
Executable 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
71
scripts/run_benchmark_quick.sh
Executable 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
104
scripts/run_benchmark_suite.sh
Executable 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 "================================================================================"
|
||||||
Reference in New Issue
Block a user