main.sh 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452
  1. #!/bin/bash
  2. # 性能测试脚本,用于顺序执行多种性能分析工具
  3. # 颜色定义
  4. RED='\033[0;31m'
  5. GREEN='\033[0;32m'
  6. YELLOW='\033[1;33m'
  7. BLUE='\033[0;34m'
  8. NC='\033[0m' # No Color
  9. # 默认参数
  10. RESULT_DIR="results"
  11. TEST_SCRIPT="" # 待测试的脚本
  12. ARGS_SCRIPT="" # 待测试脚本的参数
  13. PSUTIL_SCRIPT="monitor_util.py" # 监测工具脚本
  14. INTEGRATED_SCRIPT="integrate_results.py" # 融合工具脚本
  15. PLOT_SCRIPT="plot_offline.py" # 画图工具脚本
  16. CPROFILE_OUTPUT="$RESULT_DIR/cprofile_output.prof" # cProfile 结果文件
  17. MEMORY_PROFILE_OUTPUT="$RESULT_DIR/mprofile_output.dat" # memory_profiler输出文件
  18. #MEMORY_PLOT_OUTPUT="$RESULT_DIR/mprofile_memory_plot.png" # memory_profiler 绘制的曲线图
  19. FLAMEGRAPH_OUTPUT="$RESULT_DIR/pyspy_output.svg" # py-spy 输出文件
  20. PSUTIL_OUTPUT="$RESULT_DIR/psutil_output.csv" # psutil 输出文件
  21. INTEGRATED_REPORT="$RESULT_DIR/performance_analysis_report.json" # 融合结果文件
  22. INTEGRATED_PLOT="$RESULT_DIR/performance_analysis_report.png" # 融合结果图
  23. # 日志函数
  24. log_info() {
  25. echo -e "${BLUE}[INFO]${NC} $1"
  26. }
  27. log_success() {
  28. echo -e "${GREEN}[SUCCESS]${NC} $1"
  29. }
  30. log_warning() {
  31. echo -e "${YELLOW}[WARNING]${NC} $1"
  32. }
  33. log_error() {
  34. echo -e "${RED}[ERROR]${NC} $1"
  35. }
  36. # 显示标题
  37. show_header() {
  38. echo "========================================="
  39. echo "Python脚本性能分析工具链"
  40. echo "========================================="
  41. sleep 2
  42. }
  43. # 显示步骤标题
  44. show_step() {
  45. echo ""
  46. echo "-----------------------------------------"
  47. echo "$1"
  48. echo "-----------------------------------------"
  49. }
  50. # 检查脚本文件是否存在
  51. check_script_exists() {
  52. if [ -f "config.json" ]; then
  53. # 读取脚本路径
  54. TEST_SCRIPT=$(python -c "
  55. import json
  56. try:
  57. with open('config.json', 'r') as f:
  58. config = json.load(f)
  59. script_path = config.get('test_script_dir', '')
  60. print(script_path)
  61. except Exception as e:
  62. print('')
  63. " 2>/dev/null)
  64. if [ -n "$TEST_SCRIPT" ]; then
  65. log_info "从 config.json 读取测试脚本: $TEST_SCRIPT"
  66. else
  67. log_warning "config.json 中未找到有效的 test_script_dir"
  68. return 1
  69. fi
  70. # 确认脚本存在
  71. if [ ! -f "$TEST_SCRIPT" ]; then
  72. log_error "测试脚本 $TEST_SCRIPT 不存在"
  73. return 1
  74. fi
  75. # 读取脚本参数
  76. ARGS_SCRIPT=$(python -c "
  77. import json
  78. try:
  79. with open('config.json', 'r') as f:
  80. config = json.load(f)
  81. args = config.get('args', {})
  82. cmd_args = ' '.join('--{} {}'.format(k, v) for k, v in args.items())
  83. print(cmd_args)
  84. except Exception as e:
  85. print('')
  86. " 2>/dev/null)
  87. if [ -n "$ARGS_SCRIPT" ]; then
  88. log_info "从 config.json 读取脚本参数: $ARGS_SCRIPT"
  89. else
  90. log_warning "config.json 中未配置有效的 args"
  91. fi
  92. else
  93. log_warning "未找到 config.json 配置文件"
  94. return 1
  95. fi
  96. sleep 2
  97. return 0
  98. }
  99. # 创建结果保存目录
  100. mk_dir(){
  101. if [ ! -d "$RESULT_DIR" ]; then
  102. mkdir -p "$RESULT_DIR"
  103. log_info "创建结果保存目录: $RESULT_DIR"
  104. else
  105. log_info "使用现有结果保存目录: $RESULT_DIR"
  106. fi
  107. }
  108. # 1. 使用cProfile进行性能分析
  109. run_cprofile() {
  110. show_step "步骤 1: 使用 cProfile 进行性能分析"
  111. log_info "准备对 $TEST_SCRIPT 进行性能分析..."
  112. # 将之前的结果删除
  113. rm -rf "$CPROFILE_OUTPUT"
  114. log_info "键入指令: python -m cProfile -o $CPROFILE_OUTPUT $TEST_SCRIPT $ARGS_SCRIPT"
  115. if python -m cProfile -o "$CPROFILE_OUTPUT" "$TEST_SCRIPT" $ARGS_SCRIPT; then
  116. log_success "cProfile 分析完成,结果保存至 $CPROFILE_OUTPUT"
  117. return 0
  118. else
  119. log_error "cProfile 分析失败"
  120. return 1
  121. fi
  122. }
  123. # 3. 使用memory_profiler分析内存使用情况
  124. run_memory_profiler() {
  125. show_step "步骤 3: 使用 memory_profiler 分析内存使用情况"
  126. # 检查是否安装了memory_profiler
  127. if ! command -v mprof &> /dev/null; then
  128. log_warning "未找到 memory_profiler (mprof),尝试安装..."
  129. if pip3 install memory-profiler -i https://pypi.tuna.tsinghua.edu.cn/simple; then
  130. log_success "memory_profiler 安装成功"
  131. echo "适配安装matplotlib: "
  132. conda install matplotlib
  133. else
  134. log_error "memory_profiler 安装失败"
  135. return 1
  136. fi
  137. fi
  138. # 将之前的结果删除
  139. rm -rf "$MEMORY_PROFILE_OUTPUT"
  140. # 运行内存分析
  141. if command -v mprof &> /dev/null; then
  142. log_info "键入指令: mprof run --output $MEMORY_PROFILE_OUTPUT $TEST_SCRIPT $ARGS_SCRIPT"
  143. if mprof run --output "$MEMORY_PROFILE_OUTPUT" "$TEST_SCRIPT" $ARGS_SCRIPT; then
  144. log_success "memory_profiler 分析完成,结果保存至 $MEMORY_PROFILE_OUTPUT"
  145. # 绘制内存占用曲线(弃用)
  146. # log_info "绘制内存占用曲线"
  147. # if MPLBACKEND=Agg mprof plot --flame "$MEMORY_PROFILE_OUTPUT" -o "$MEMORY_PLOT_OUTPUT"; then
  148. # log_success "绘制完成,结果保存至 $MEMORY_PLOT_OUTPUT"
  149. # else
  150. # log_error "绘制失败"
  151. # fi
  152. else
  153. log_error "memory_profiler 分析失败"
  154. fi
  155. else
  156. log_warning "无法安装 memory_profiler,跳过内存分析步骤"
  157. fi
  158. }
  159. # 2. 使用py-spy记录性能数据
  160. run_py_spy() {
  161. show_step "步骤 2: 使用 py-spy record 进行性能分析"
  162. # 检查是否安装了py-spy
  163. if ! command -v py-spy &> /dev/null; then
  164. log_warning "未找到 py-spy ,尝试安装..."
  165. if pip3 install py-spy -i https://pypi.tuna.tsinghua.edu.cn/simple; then
  166. log_success "py-spy 安装成功"
  167. else
  168. log_error "py-spy 安装失败"
  169. return 1
  170. fi
  171. fi
  172. # 再次检查是否安装成功
  173. if ! command -v py-spy &> /dev/null; then
  174. log_error "py-spy 安装失败"
  175. return 1
  176. fi
  177. # 删除之前的结果
  178. rm -rf "$FLAMEGRAPH_OUTPUT"
  179. # 根据操作系统类型选择合适的py-spy参数,需要统计子线程和C拓展
  180. if [[ "$OSTYPE" == "msys" ]] || [[ "$OSTYPE" == "win32" ]]; then
  181. # Windows环境下使用--nonblocking选项
  182. log_info "Windows环境下使用--nonblocking选项"
  183. PYSPY_CMD="py-spy record -o $FLAMEGRAPH_OUTPUT --duration 35 --native --subprocesses --nonblocking -- python $TEST_SCRIPT $ARGS_SCRIPT"
  184. elif [[ "$OSTYPE" == "linux-gnu"* ]]; then
  185. # Linux环境下使用sudo运行
  186. log_info "Linux环境下使用sudo运行py-spy"
  187. PYSPY_CMD="sudo env \"PATH=\$PATH\" py-spy record -o $FLAMEGRAPH_OUTPUT --duration 35 --native --subprocesses -- python $TEST_SCRIPT $ARGS_SCRIPT"
  188. else
  189. # 其他Unix系统使用默认选项
  190. PYSPY_CMD="py-spy record -o $FLAMEGRAPH_OUTPUT --duration 35 --native --subprocesses -- python $TEST_SCRIPT $ARGS_SCRIPT"
  191. fi
  192. log_info "执行命令: $PYSPY_CMD"
  193. eval "$PYSPY_CMD"
  194. PYSPY_PID=$!
  195. if wait $PYSPY_PID; then
  196. log_success "py-spy record 分析完成,火焰图保存至 $FLAMEGRAPH_OUTPUT"
  197. return 0
  198. else
  199. log_error "py-spy record 分析失败"
  200. if [[ "$OSTYPE" == "linux-gnu"* ]]; then
  201. log_warning "提示: 可能需要管理员权限,请使用 'sudo ./$0' 运行此脚本"
  202. fi
  203. return 1
  204. fi
  205. }
  206. # 4. 使用psutil获取进程信息
  207. run_psutil() {
  208. show_step "步骤 4: 使用 psutil 获取进程信息"
  209. # 检查是否安装了psutil
  210. if ! python -c "import psutil" &> /dev/null; then
  211. log_warning "未找到 psutil ,尝试安装..."
  212. if pip3 install psutil -i https://pypi.tuna.tsinghua.edu.cn/simple; then
  213. log_success "psutil 安装成功"
  214. else
  215. log_error "psutil 安装失败"
  216. return 1
  217. fi
  218. fi
  219. # 将之前的结果文件删除
  220. rm -rf "$PSUTIL_OUTPUT"
  221. # 在后台运行test_script.py并获取其PID
  222. log_info "在后台启动 $TEST_SCRIPT $ARGS_SCRIP ..."
  223. python "$TEST_SCRIPT" $ARGS_SCRIPT &
  224. TEST_PID=$!
  225. # 等待一段时间确保进程已经完全启动
  226. sleep 2
  227. # 验证进程是否仍在运行
  228. if ! kill -0 $TEST_PID 2>/dev/null; then
  229. log_error "测试脚本启动失败"
  230. return 1
  231. fi
  232. log_info "测试脚本 PID: $TEST_PID"
  233. # 使用monitor.py监控该进程
  234. log_info "开始监控进程资源使用情况..."
  235. if [[ "$OSTYPE" == "linux-gnu"* ]]; then
  236. # Linux环境下使用sudo运行monitor.py
  237. sudo env "PATH=$PATH" python "$PSUTIL_SCRIPT" --pid $TEST_PID --output "$PSUTIL_OUTPUT" &
  238. else
  239. python "$PSUTIL_SCRIPT" --pid $TEST_PID --output "$PSUTIL_OUTPUT" &
  240. fi
  241. MONITOR_PID=$!
  242. # 等待test_script.py执行完成
  243. wait $TEST_PID
  244. sleep 4
  245. log_success "测试脚本执行完成"
  246. # 终止monitor.py
  247. kill $MONITOR_PID 2>/dev/null || true
  248. log_success "资源监控完成,结果保存至 $PSUTIL_OUTPUT"
  249. }
  250. # 自动打开SVG文件
  251. open_svg_file() {
  252. if [ -f "$FLAMEGRAPH_OUTPUT" ]; then
  253. log_info "正在尝试在浏览器中打开火焰图..."
  254. # 检测是否在WSL环境中
  255. if grep -qE "(Microsoft|WSL)" /proc/version 2>/dev/null; then
  256. # WSL环境 - 使用PowerShell打开
  257. log_info "检测到WSL环境,使用PowerShell打开文件"
  258. if powershell.exe -Command "Start-Process '$(wslpath -w "$(pwd)/$FLAMEGRAPH_OUTPUT")'" 2>/dev/null; then
  259. log_success "SVG文件已在Windows默认应用中打开"
  260. return 0
  261. elif cmd.exe /c "start $(wslpath -w "$(pwd)/$FLAMEGRAPH_OUTPUT")" 2>/dev/null; then
  262. log_success "SVG文件已在Windows默认应用中打开"
  263. return 0
  264. else
  265. log_warning "无法在Windows主机上打开文件,请手动打开 $FLAMEGRAPH_OUTPUT"
  266. fi
  267. elif command -v xdg-open >/dev/null 2>&1; then
  268. # 标准Linux环境
  269. if xdg-open "$FLAMEGRAPH_OUTPUT"; then
  270. log_success "SVG文件已在默认应用中打开"
  271. return 0
  272. else
  273. log_warning "无法使用xdg-open打开文件"
  274. fi
  275. elif command -v gnome-open >/dev/null 2>&1; then
  276. if gnome-open "$FLAMEGRAPH_OUTPUT"; then
  277. log_success "SVG文件已在默认应用中打开"
  278. return 0
  279. else
  280. log_warning "无法使用gnome-open打开文件"
  281. fi
  282. else
  283. log_warning "找不到合适的打开命令,请手动在浏览器中打开 $FLAMEGRAPH_OUTPUT"
  284. fi
  285. fi
  286. return 1
  287. }
  288. # 自动打开prof文件
  289. open_prof_file() {
  290. # 检查snakeviz是否存在,不存在就尝试自动安装
  291. if [ -f "$CPROFILE_OUTPUT" ]; then
  292. log_info "自动打开cProfile分析结果"
  293. if ! command -v snakeviz &> /dev/null; then
  294. log_warning "未找到snakeviz,尝试安装..."
  295. if pip3 install snakeviz -i https://pypi.tuna.tsinghua.edu.cn/simple; then
  296. log_success "snakeviz 安装成功"
  297. else
  298. log_error "snakeviz 安装失败"
  299. return 1
  300. fi
  301. fi
  302. if command -v snakeviz &> /dev/null; then
  303. log_info "正在尝试使用snakeviz打开prof文件..."
  304. if snakeviz "$CPROFILE_OUTPUT"; then
  305. log_success "prof文件浏览完成"
  306. return 0
  307. else
  308. log_warning "无法使用snakeviz打开prof文件"
  309. fi
  310. else
  311. log_warning "snakeviz未安装,请手动打开 $CPROFILE_OUTPUT"
  312. fi
  313. else
  314. log_error "$CPROFILE_OUTPUT 文件不存在"
  315. fi
  316. return 1
  317. }
  318. # 显示结果摘要
  319. show_summary() {
  320. show_step "性能分析结果摘要"
  321. echo "1. cProfile 输出: $CPROFILE_OUTPUT (可用于 pstats 分析)"
  322. echo "2. py-spy 火焰图: $FLAMEGRAPH_OUTPUT (可在浏览器中打开查看)"
  323. echo "3. memory_profiler 输出: $MEMORY_PROFILE_OUTPUT (内存使用情况)"
  324. echo "4. psutil 输出: $PSUTIL_OUTPUT (硬件资源使用情况)"
  325. log_success "性能分析工具链执行完毕!"
  326. }
  327. # 整合所有分析结果
  328. integrate_all_results(){
  329. show_step "步骤5 整合所有分析结果"
  330. # 检查python脚本是否存在
  331. if [ ! -f "$INTEGRATED_SCRIPT" ]; then
  332. log_warning "整合脚本 $INTEGRATED_SCRIPT 不存在"
  333. return 1
  334. else
  335. echo "结果整合中..."
  336. # 构建命令参数
  337. CMD="python $INTEGRATED_SCRIPT"
  338. if [ -f "$CPROFILE_OUTPUT" ]; then
  339. CMD="$CMD --cprofile $CPROFILE_OUTPUT"
  340. fi
  341. if [ -f "$MEMORY_PROFILE_OUTPUT" ]; then
  342. CMD="$CMD --memory $MEMORY_PROFILE_OUTPUT"
  343. fi
  344. if [ -f "$FLAMEGRAPH_OUTPUT" ]; then
  345. CMD="$CMD --flamegraph $FLAMEGRAPH_OUTPUT"
  346. fi
  347. if [ -f "$PSUTIL_OUTPUT" ]; then
  348. CMD="$CMD --psutil $PSUTIL_OUTPUT"
  349. fi
  350. CMD="$CMD --output $INTEGRATED_REPORT"
  351. # 执行整合脚本
  352. log_info "执行整合命令: $CMD"
  353. if eval "$CMD"; then
  354. log_success "所有分析结果已整合至 $INTEGRATED_REPORT"
  355. else
  356. log_error "整合过程出错"
  357. # 检查pandas是否存在
  358. if ! python -c "import pandas" &> /dev/null; then
  359. log_warning "未找到pandas,尝试安装..."
  360. if pip3 install pandas -i https://pypi.tuna.tsinghua.edu.cn/simple; then
  361. log_success "pandas 安装成功"
  362. else
  363. log_error "pandas 安装失败"
  364. return 1
  365. fi
  366. # 尝试再次执行命令
  367. if eval "$CMD"; then
  368. log_success "所有分析结果已整合至 $INTEGRATED_REPORT"
  369. else
  370. log_error "无法执行整合命令"
  371. return 1
  372. fi
  373. else
  374. return 1
  375. fi
  376. fi
  377. # 绘制融合结果图
  378. if [ -f "$PLOT_SCRIPT" ]; then
  379. log_info "绘制融合结果图"
  380. if python "$PLOT_SCRIPT" --input "$INTEGRATED_REPORT" --output "$INTEGRATED_PLOT"; then
  381. log_success "融合结果图已保存至 $INTEGRATED_PLOT"
  382. else
  383. log_error "无法绘制融合结果图"
  384. return 1
  385. fi
  386. else
  387. log_warning "未找到绘制脚本 $PLOT_SCRIPT"
  388. return 1
  389. fi
  390. fi
  391. return 0
  392. }
  393. # 主函数
  394. main() {
  395. # 显示标题
  396. show_header
  397. # 检查脚本是否存在
  398. if ! check_script_exists; then
  399. log_error "请检查测试脚本是否存在."
  400. exit 1
  401. fi
  402. # 创建结果保存目录
  403. mk_dir
  404. # 执行各项分析
  405. run_cprofile # cProfile 算法性能分析,统计主线程的函数调用次数和运行时间
  406. run_py_spy # py-spy 火焰图
  407. run_memory_profiler # 内存使用情况分析
  408. run_psutil # 使用psutil监控进程资源使用情况
  409. show_summary # 显示结果摘要
  410. integrate_all_results # 整合所有结果
  411. # 自动打开结果文件
  412. open_svg_file
  413. open_prof_file
  414. }
  415. # 执行主函数
  416. main "$@"