| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416 |
- #!/bin/bash
- # 性能测试脚本,用于顺序执行多种性能分析工具
- # 颜色定义
- RED='\033[0;31m'
- GREEN='\033[0;32m'
- YELLOW='\033[1;33m'
- BLUE='\033[0;34m'
- NC='\033[0m' # No Color
- # 默认参数
- RESULT_DIR="results"
- TEST_SCRIPT="" # 待测试的脚本
- PSUTIL_SCRIPT="monitor_util.py" # 监测工具脚本
- INTEGRATED_SCRIPT="integrate_results.py" # 融合工具脚本
- CPROFILE_OUTPUT="$RESULT_DIR/cprofile_output.prof" # cProfile 结果文件
- MEMORY_PROFILE_OUTPUT="$RESULT_DIR/mprofile_output.dat" # memory_profiler输出文件
- MEMORY_PLOT_OUTPUT="$RESULT_DIR/mprofile_memory_plot.png" # memory_profiler 绘制的曲线图
- FLAMEGRAPH_OUTPUT="$RESULT_DIR/pyspy_output.svg" # py-spy 输出文件
- PSUTIL_OUTPUT="$RESULT_DIR/psutil_output.csv" # psutil 输出文件
- INTEGRATED_REPORT="$RESULT_DIR/performance_analysis_report.json" # 融合结果文件
- # 日志函数
- log_info() {
- echo -e "${BLUE}[INFO]${NC} $1"
- }
- log_success() {
- echo -e "${GREEN}[SUCCESS]${NC} $1"
- }
- log_warning() {
- echo -e "${YELLOW}[WARNING]${NC} $1"
- }
- log_error() {
- echo -e "${RED}[ERROR]${NC} $1"
- }
- # 显示标题
- show_header() {
- echo "========================================="
- echo "Python脚本性能分析工具链"
- echo "========================================="
- sleep 2
- }
- # 显示步骤标题
- show_step() {
- echo ""
- echo "-----------------------------------------"
- echo "$1"
- echo "-----------------------------------------"
- }
- # 检查脚本文件是否存在
- check_script_exists() {
- if [ -f "config.json" ]; then
- TEST_SCRIPT=$(python -c "
- import json
- try:
- with open('config.json', 'r') as f:
- config = json.load(f)
- print(config.get('test_script_dir', ''))
- except Exception as e:
- print('')
- " 2>/dev/null)
- if [ -n "$TEST_SCRIPT" ]; then
- log_info "从 config.json 读取测试脚本路径: $TEST_SCRIPT"
- else
- log_warning "config.json 中未找到有效的 test_script_dir 配置"
- return 1
- fi
- else
- log_warning "未找到 config.json 配置文件"
- return 1
- fi
- if [ ! -f "$TEST_SCRIPT" ]; then
- log_error "测试脚本 $TEST_SCRIPT 不存在"
- return 1
- fi
- sleep 2
- return 0
- }
- # 创建结果保存目录
- mk_dir(){
- if [ ! -d "$RESULT_DIR" ]; then
- mkdir -p "$RESULT_DIR"
- log_info "创建结果保存目录: $RESULT_DIR"
- else
- log_info "使用现有结果保存目录: $RESULT_DIR"
- fi
- }
- # 1. 使用cProfile进行性能分析
- run_cprofile() {
- show_step "步骤 1: 使用 cProfile 进行性能分析"
- log_info "准备对 $TEST_SCRIPT 进行性能分析..."
-
- # 确保使用绝对路径
- PROFILE_ABS_PATH="$(pwd)/$CPROFILE_OUTPUT"
-
- if python -m cProfile -o "$PROFILE_ABS_PATH" "$TEST_SCRIPT"; then
- log_success "cProfile 分析完成,结果保存至 $PROFILE_ABS_PATH"
- return 0
- else
- log_error "cProfile 分析失败"
- return 1
- fi
- }
- # 3. 使用memory_profiler分析内存使用情况
- run_memory_profiler() {
- show_step "步骤 3: 使用 memory_profiler 分析内存使用情况"
-
- # 检查是否安装了memory_profiler
- if ! command -v mprof &> /dev/null; then
- log_warning "未找到 memory_profiler (mprof),尝试安装..."
- if pip3 install memory-profiler -i https://pypi.tuna.tsinghua.edu.cn/simple; then
- log_success "memory_profiler 安装成功"
- echo "适配安装matplotlib: "
- conda install matplotlib
- else
- log_error "memory_profiler 安装失败"
- return 1
- fi
- fi
- # 运行内存分析
- if command -v mprof &> /dev/null; then
- if mprof run --output "$MEMORY_PROFILE_OUTPUT" "$TEST_SCRIPT"; then
- log_success "memory_profiler 分析完成,结果保存至 $MEMORY_PROFILE_OUTPUT"
-
- # 绘制内存占用曲线
- log_info "绘制内存占用曲线"
- if MPLBACKEND=Agg mprof plot --flame "$MEMORY_PROFILE_OUTPUT" -o "$MEMORY_PLOT_OUTPUT"; then
- log_success "绘制完成,结果保存至 $MEMORY_PLOT_OUTPUT"
- else
- log_error "绘制失败"
- fi
- else
- log_error "memory_profiler 分析失败"
- fi
- else
- log_warning "无法安装 memory_profiler,跳过内存分析步骤"
- fi
- }
- # 2. 使用py-spy记录性能数据
- run_py_spy() {
- show_step "步骤 2: 使用 py-spy record 进行性能分析"
- # 检查是否安装了py-spy
- if ! command -v py-spy &> /dev/null; then
- log_warning "未找到 py-spy ,尝试安装..."
- if pip3 install py-spy -i https://pypi.tuna.tsinghua.edu.cn/simple; then
- log_success "py-spy 安装成功"
- else
- log_error "py-spy 安装失败"
- return 1
- fi
- fi
- # 再次检查是否安装成功
- if ! command -v py-spy &> /dev/null; then
- log_error "py-spy 安装失败"
- return 1
- fi
- # 根据操作系统类型选择合适的py-spy参数,需要统计子线程和C拓展
- if [[ "$OSTYPE" == "msys" ]] || [[ "$OSTYPE" == "win32" ]]; then
- # Windows环境下使用--nonblocking选项
- log_info "Windows环境下使用--nonblocking选项"
- PYSPY_CMD="py-spy record -o $FLAMEGRAPH_OUTPUT --duration 35 --native --subprocesses --nonblocking -- python $TEST_SCRIPT"
- elif [[ "$OSTYPE" == "linux-gnu"* ]]; then
- # Linux环境下使用sudo运行
- log_info "Linux环境下使用sudo运行py-spy"
- PYSPY_CMD="sudo env \"PATH=\$PATH\" py-spy record -o $FLAMEGRAPH_OUTPUT --duration 35 --native --subprocesses -- python $TEST_SCRIPT"
- else
- # 其他Unix系统使用默认选项
- PYSPY_CMD="py-spy record -o $FLAMEGRAPH_OUTPUT --duration 35 --native --subprocesses -- python $TEST_SCRIPT"
- fi
-
- log_info "执行命令: $PYSPY_CMD"
- eval "$PYSPY_CMD"
- PYSPY_PID=$!
- if wait $PYSPY_PID; then
- log_success "py-spy record 分析完成,火焰图保存至 $FLAMEGRAPH_OUTPUT"
- return 0
- else
- log_error "py-spy record 分析失败"
- if [[ "$OSTYPE" == "linux-gnu"* ]]; then
- log_warning "提示: 可能需要管理员权限,请使用 'sudo ./$0' 运行此脚本"
- fi
- return 1
- fi
- }
- # 4. 使用psutil获取进程信息
- run_psutil() {
- show_step "步骤 4: 使用 psutil 获取进程信息"
-
- # 检查是否安装了psutil
- if ! python -c "import psutil" &> /dev/null; then
- log_warning "未找到 psutil ,尝试安装..."
- if pip3 install psutil -i https://pypi.tuna.tsinghua.edu.cn/simple; then
- log_success "psutil 安装成功"
- else
- log_error "psutil 安装失败"
- return 1
- fi
- fi
- # 在后台运行test_script.py并获取其PID
- log_info "在后台启动 $TEST_SCRIPT ..."
- python "$TEST_SCRIPT" &
- TEST_PID=$!
-
- # 等待一段时间确保进程已经完全启动
- sleep 2
-
- # 验证进程是否仍在运行
- if ! kill -0 $TEST_PID 2>/dev/null; then
- log_error "测试脚本启动失败"
- return 1
- fi
-
- log_info "测试脚本 PID: $TEST_PID"
-
- # 使用monitor.py监控该进程
- log_info "开始监控进程资源使用情况..."
- if [[ "$OSTYPE" == "linux-gnu"* ]]; then
- # Linux环境下使用sudo运行monitor.py
- sudo env "PATH=$PATH" python "$PSUTIL_SCRIPT" --pid $TEST_PID --output "$PSUTIL_OUTPUT" &
- else
- python "$PSUTIL_SCRIPT" --pid $TEST_PID --output "$PSUTIL_OUTPUT" &
- fi
- MONITOR_PID=$!
-
- # 等待test_script.py执行完成
- wait $TEST_PID
- sleep 4
- log_success "测试脚本执行完成"
- # 终止monitor.py
- kill $MONITOR_PID 2>/dev/null || true
-
- log_success "资源监控完成,结果保存至 $PSUTIL_OUTPUT"
- }
- # 自动打开SVG文件
- open_svg_file() {
- if [ -f "$FLAMEGRAPH_OUTPUT" ]; then
- log_info "正在尝试在浏览器中打开火焰图..."
-
- # 检测是否在WSL环境中
- if grep -qE "(Microsoft|WSL)" /proc/version 2>/dev/null; then
- # WSL环境 - 使用PowerShell打开
- log_info "检测到WSL环境,使用PowerShell打开文件"
- if powershell.exe -Command "Start-Process '$(wslpath -w "$(pwd)/$FLAMEGRAPH_OUTPUT")'" 2>/dev/null; then
- log_success "SVG文件已在Windows默认应用中打开"
- return 0
- elif cmd.exe /c "start $(wslpath -w "$(pwd)/$FLAMEGRAPH_OUTPUT")" 2>/dev/null; then
- log_success "SVG文件已在Windows默认应用中打开"
- return 0
- else
- log_warning "无法在Windows主机上打开文件,请手动打开 $FLAMEGRAPH_OUTPUT"
- fi
- elif command -v xdg-open >/dev/null 2>&1; then
- # 标准Linux环境
- if xdg-open "$FLAMEGRAPH_OUTPUT"; then
- log_success "SVG文件已在默认应用中打开"
- return 0
- else
- log_warning "无法使用xdg-open打开文件"
- fi
- elif command -v gnome-open >/dev/null 2>&1; then
- if gnome-open "$FLAMEGRAPH_OUTPUT"; then
- log_success "SVG文件已在默认应用中打开"
- return 0
- else
- log_warning "无法使用gnome-open打开文件"
- fi
- else
- log_warning "找不到合适的打开命令,请手动在浏览器中打开 $FLAMEGRAPH_OUTPUT"
- fi
- fi
- return 1
- }
- # 自动打开prof文件
- open_prof_file() {
- # 检查snakeviz是否存在,不存在就尝试自动安装
- if [ -f "$CPROFILE_OUTPUT" ]; then
- log_info "自动打开cProfile分析结果"
- if ! command -v snakeviz &> /dev/null; then
- log_warning "未找到snakeviz,尝试安装..."
- if pip3 install snakeviz -i https://pypi.tuna.tsinghua.edu.cn/simple; then
- log_success "snakeviz 安装成功"
- else
- log_error "snakeviz 安装失败"
- return 1
- fi
- fi
- if command -v snakeviz &> /dev/null; then
- log_info "正在尝试使用snakeviz打开prof文件..."
- if snakeviz "$CPROFILE_OUTPUT"; then
- log_success "prof文件浏览完成"
- return 0
- else
- log_warning "无法使用snakeviz打开prof文件"
- fi
- else
- log_warning "snakeviz未安装,请手动打开 $CPROFILE_OUTPUT"
- fi
- else
- log_error "$CPROFILE_OUTPUT 文件不存在"
- fi
- return 1
- }
- # 显示结果摘要
- show_summary() {
- show_step "性能分析结果摘要"
- echo "1. cProfile 输出: $CPROFILE_OUTPUT (可用于 pstats 分析)"
- echo "2. py-spy 火焰图: $FLAMEGRAPH_OUTPUT (可在浏览器中打开查看)"
- echo "3. memory_profiler 输出: $MEMORY_PROFILE_OUTPUT (内存使用情况)"
- echo "4. psutil 输出: $PSUTIL_OUTPUT (硬件资源使用情况)"
- log_success "性能分析工具链执行完毕!"
- }
- # 整合所有分析结果
- integrate_all_results(){
- show_step "步骤5 整合所有分析结果"
- # 检查python脚本是否存在
- if [ ! -f "$INTEGRATED_SCRIPT" ]; then
- log_warning "整合脚本 $INTEGRATED_SCRIPT 不存在"
- return 1
- else
- echo "结果整合中..."
- # 构建命令参数
- CMD="python $INTEGRATED_SCRIPT"
- if [ -f "$CPROFILE_OUTPUT" ]; then
- CMD="$CMD --cprofile $CPROFILE_OUTPUT"
- fi
- if [ -f "$MEMORY_PROFILE_OUTPUT" ]; then
- CMD="$CMD --memory $MEMORY_PROFILE_OUTPUT"
- fi
- if [ -f "$FLAMEGRAPH_OUTPUT" ]; then
- CMD="$CMD --flamegraph $FLAMEGRAPH_OUTPUT"
- fi
- if [ -f "$PSUTIL_OUTPUT" ]; then
- CMD="$CMD --psutil $PSUTIL_OUTPUT"
- fi
- CMD="$CMD --output $INTEGRATED_REPORT"
- # 执行整合脚本
- log_info "执行整合命令: $CMD"
- if eval "$CMD"; then
- log_success "所有分析结果已整合至 $INTEGRATED_REPORT"
- return 0
- else
- log_error "整合过程出错"
- # 检查pandas是否存在
- if ! python -c "import pandas" &> /dev/null; then
- log_warning "未找到pandas,尝试安装..."
- if pip3 install pandas -i https://pypi.tuna.tsinghua.edu.cn/simple; then
- log_success "pandas 安装成功"
- else
- log_error "pandas 安装失败"
- return 1
- fi
- # 尝试再次执行命令
- if eval "$CMD"; then
- log_success "所有分析结果已整合至 $INTEGRATED_REPORT"
- return 0
- fi
- fi
- return 1
- fi
- fi
- }
- # 主函数
- main() {
- # 显示标题
- show_header
- # 检查脚本是否存在
- if ! check_script_exists; then
- log_error "请检查测试脚本是否存在."
- exit 1
- fi
- # 创建结果保存目录
- mk_dir
- # 执行各项分析
- run_cprofile # cProfile 算法性能分析,统计主线程的函数调用次数和运行时间
- run_py_spy # py-spy 火焰图
- run_memory_profiler # 内存使用情况分析
- run_psutil # 使用psutil监控进程资源使用情况
- show_summary # 显示结果摘要
- integrate_all_results # 整合所有结果
- # 自动打开结果文件
- open_svg_file
- open_prof_file
- }
- # 执行主函数
- main "$@"
|