main.sh 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  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. # 将之前的结果删除
  127. rm -rf "$MEMORY_PROFILE_OUTPUT"
  128. # 运行内存分析
  129. if command -v mprof &> /dev/null; then
  130. log_info "键入指令: mprof run --output $MEMORY_PROFILE_OUTPUT $TEST_SCRIPT $ARGS_SCRIPT"
  131. if mprof run --output "$MEMORY_PROFILE_OUTPUT" "$TEST_SCRIPT" $ARGS_SCRIPT; then
  132. log_success "memory_profiler 分析完成,结果保存至 $MEMORY_PROFILE_OUTPUT"
  133. # 绘制内存占用曲线(弃用)
  134. # log_info "绘制内存占用曲线"
  135. # if MPLBACKEND=Agg mprof plot --flame "$MEMORY_PROFILE_OUTPUT" -o "$MEMORY_PLOT_OUTPUT"; then
  136. # log_success "绘制完成,结果保存至 $MEMORY_PLOT_OUTPUT"
  137. # else
  138. # log_error "绘制失败"
  139. # fi
  140. else
  141. log_error "memory_profiler 分析失败"
  142. fi
  143. else
  144. log_warning "无法安装 memory_profiler,跳过内存分析步骤"
  145. fi
  146. }
  147. # 2. 使用py-spy记录性能数据
  148. run_py_spy() {
  149. show_step "步骤 2: 使用 py-spy record 进行性能分析"
  150. # 删除之前的结果
  151. rm -rf "$FLAMEGRAPH_OUTPUT"
  152. # 根据操作系统类型选择合适的py-spy参数,需要统计子线程和C拓展
  153. if [[ "$OSTYPE" == "msys" ]] || [[ "$OSTYPE" == "win32" ]]; then
  154. # Windows环境下使用--nonblocking选项
  155. log_info "Windows环境下使用--nonblocking选项"
  156. PYSPY_CMD="py-spy record -o $FLAMEGRAPH_OUTPUT --duration 35 --native --subprocesses --nonblocking -- python $TEST_SCRIPT $ARGS_SCRIPT"
  157. elif [[ "$OSTYPE" == "linux-gnu"* ]]; then
  158. # Linux环境下使用sudo运行
  159. log_info "Linux环境下使用sudo运行py-spy"
  160. PYSPY_CMD="sudo env \"PATH=\$PATH\" py-spy record -o $FLAMEGRAPH_OUTPUT --duration 35 --native --subprocesses -- python $TEST_SCRIPT $ARGS_SCRIPT"
  161. else
  162. # 其他Unix系统使用默认选项
  163. PYSPY_CMD="py-spy record -o $FLAMEGRAPH_OUTPUT --duration 35 --native --subprocesses -- python $TEST_SCRIPT $ARGS_SCRIPT"
  164. fi
  165. log_info "执行命令: $PYSPY_CMD"
  166. eval "$PYSPY_CMD"
  167. PYSPY_PID=$!
  168. if wait $PYSPY_PID; then
  169. log_success "py-spy record 分析完成,火焰图保存至 $FLAMEGRAPH_OUTPUT"
  170. return 0
  171. else
  172. log_error "py-spy record 分析失败"
  173. if [[ "$OSTYPE" == "linux-gnu"* ]]; then
  174. log_warning "提示: 可能需要管理员权限,请使用 'sudo ./$0' 运行此脚本"
  175. fi
  176. return 1
  177. fi
  178. }
  179. # 4. 使用psutil获取进程信息
  180. run_psutil() {
  181. show_step "步骤 4: 使用 psutil 获取进程信息"
  182. # 将之前的结果文件删除
  183. rm -rf "$PSUTIL_OUTPUT"
  184. # 在后台运行test_script.py并获取其PID
  185. log_info "在后台启动 $TEST_SCRIPT $ARGS_SCRIP ..."
  186. python "$TEST_SCRIPT" $ARGS_SCRIPT &
  187. TEST_PID=$!
  188. # 等待一段时间确保进程已经完全启动
  189. sleep 2
  190. # 验证进程是否仍在运行
  191. if ! kill -0 $TEST_PID 2>/dev/null; then
  192. log_error "测试脚本启动失败"
  193. return 1
  194. fi
  195. log_info "测试脚本 PID: $TEST_PID"
  196. # 使用monitor.py监控该进程
  197. log_info "开始监控进程资源使用情况..."
  198. if [[ "$OSTYPE" == "linux-gnu"* ]]; then
  199. # Linux环境下使用sudo运行monitor.py
  200. sudo env "PATH=$PATH" python "$PSUTIL_SCRIPT" --pid $TEST_PID --output "$PSUTIL_OUTPUT" &
  201. else
  202. python "$PSUTIL_SCRIPT" --pid $TEST_PID --output "$PSUTIL_OUTPUT" &
  203. fi
  204. MONITOR_PID=$!
  205. # 等待test_script.py执行完成
  206. wait $TEST_PID
  207. sleep 4
  208. log_success "测试脚本执行完成"
  209. # 终止monitor.py
  210. kill $MONITOR_PID 2>/dev/null || true
  211. log_success "资源监控完成,结果保存至 $PSUTIL_OUTPUT"
  212. }
  213. # 0.检查环境完备性
  214. check_env(){
  215. # 检查是否安装了py-spy
  216. if ! command -v py-spy &> /dev/null; then
  217. log_warning "未找到 py-spy ,尝试安装..."
  218. if pip3 install py-spy -i https://pypi.tuna.tsinghua.edu.cn/simple; then
  219. log_success "py-spy 安装成功"
  220. else
  221. log_error "py-spy 安装失败"
  222. return 1
  223. fi
  224. fi
  225. # 检查是否安装了memory_profiler
  226. if ! command -v mprof &> /dev/null; then
  227. log_warning "未找到 memory_profiler (mprof),尝试安装..."
  228. if pip3 install memory-profiler -i https://pypi.tuna.tsinghua.edu.cn/simple; then
  229. log_success "memory_profiler 安装成功"
  230. echo "适配安装matplotlib: "
  231. conda install matplotlib
  232. else
  233. log_error "memory_profiler 安装失败"
  234. return 1
  235. fi
  236. fi
  237. # 检查是否安装了psutil
  238. if ! python -c "import psutil" &> /dev/null; then
  239. log_warning "未找到 psutil ,尝试安装..."
  240. if pip3 install psutil -i https://pypi.tuna.tsinghua.edu.cn/simple; then
  241. log_success "psutil 安装成功"
  242. else
  243. log_error "psutil 安装失败"
  244. return 1
  245. fi
  246. fi
  247. # 检查是否安装nvidia-ml-py
  248. if ! python -c "import pynvml" &> /dev/null; then
  249. log_warning "未找到 nvidia_ml_py ,尝试安装..."
  250. if pip3 install nvidia-ml-py -i https://pypi.tuna.tsinghua.edu.cn/simple; then
  251. log_success "nvidia_ml_py 安装成功"
  252. else
  253. log_error "nvidia_ml_py 安装失败"
  254. return 1
  255. fi
  256. fi
  257. }
  258. # 自动打开SVG文件
  259. open_svg_file() {
  260. if [ -f "$FLAMEGRAPH_OUTPUT" ]; then
  261. log_info "正在尝试在浏览器中打开火焰图..."
  262. # 检测是否在WSL环境中
  263. if grep -qE "(Microsoft|WSL)" /proc/version 2>/dev/null; then
  264. # WSL环境 - 使用PowerShell打开
  265. log_info "检测到WSL环境,使用PowerShell打开文件"
  266. if powershell.exe -Command "Start-Process '$(wslpath -w "$(pwd)/$FLAMEGRAPH_OUTPUT")'" 2>/dev/null; then
  267. log_success "SVG文件已在Windows默认应用中打开"
  268. return 0
  269. elif cmd.exe /c "start $(wslpath -w "$(pwd)/$FLAMEGRAPH_OUTPUT")" 2>/dev/null; then
  270. log_success "SVG文件已在Windows默认应用中打开"
  271. return 0
  272. else
  273. log_warning "无法在Windows主机上打开文件,请手动打开 $FLAMEGRAPH_OUTPUT"
  274. fi
  275. elif command -v xdg-open >/dev/null 2>&1; then
  276. # 标准Linux环境
  277. if xdg-open "$FLAMEGRAPH_OUTPUT"; then
  278. log_success "SVG文件已在默认应用中打开"
  279. return 0
  280. else
  281. log_warning "无法使用xdg-open打开文件"
  282. fi
  283. elif command -v gnome-open >/dev/null 2>&1; then
  284. if gnome-open "$FLAMEGRAPH_OUTPUT"; then
  285. log_success "SVG文件已在默认应用中打开"
  286. return 0
  287. else
  288. log_warning "无法使用gnome-open打开文件"
  289. fi
  290. else
  291. log_warning "找不到合适的打开命令,请手动在浏览器中打开 $FLAMEGRAPH_OUTPUT"
  292. fi
  293. fi
  294. return 1
  295. }
  296. # 自动打开prof文件
  297. open_prof_file() {
  298. # 检查snakeviz是否存在,不存在就尝试自动安装
  299. if [ -f "$CPROFILE_OUTPUT" ]; then
  300. log_info "自动打开cProfile分析结果"
  301. if ! command -v snakeviz &> /dev/null; then
  302. log_warning "未找到snakeviz,尝试安装..."
  303. if pip3 install snakeviz -i https://pypi.tuna.tsinghua.edu.cn/simple; then
  304. log_success "snakeviz 安装成功"
  305. else
  306. log_error "snakeviz 安装失败"
  307. return 1
  308. fi
  309. fi
  310. if command -v snakeviz &> /dev/null; then
  311. log_info "正在尝试使用snakeviz打开prof文件..."
  312. if snakeviz "$CPROFILE_OUTPUT"; then
  313. log_success "prof文件浏览完成"
  314. return 0
  315. else
  316. log_warning "无法使用snakeviz打开prof文件"
  317. fi
  318. else
  319. log_warning "snakeviz未安装,请手动打开 $CPROFILE_OUTPUT"
  320. fi
  321. else
  322. log_error "$CPROFILE_OUTPUT 文件不存在"
  323. fi
  324. return 1
  325. }
  326. # 显示结果摘要
  327. show_summary() {
  328. show_step "性能分析结果摘要"
  329. echo "1. cProfile 输出: $CPROFILE_OUTPUT (可用于 pstats 分析)"
  330. echo "2. py-spy 火焰图: $FLAMEGRAPH_OUTPUT (可在浏览器中打开查看)"
  331. echo "3. memory_profiler 输出: $MEMORY_PROFILE_OUTPUT (内存使用情况)"
  332. echo "4. psutil 输出: $PSUTIL_OUTPUT (硬件资源使用情况)"
  333. log_success "性能分析工具链执行完毕!"
  334. }
  335. # 整合所有分析结果
  336. integrate_all_results(){
  337. show_step "步骤5 整合所有分析结果"
  338. # 检查python脚本是否存在
  339. if [ ! -f "$INTEGRATED_SCRIPT" ]; then
  340. log_warning "整合脚本 $INTEGRATED_SCRIPT 不存在"
  341. return 1
  342. else
  343. echo "结果整合中..."
  344. # 构建命令参数
  345. CMD="python $INTEGRATED_SCRIPT"
  346. if [ -f "$CPROFILE_OUTPUT" ]; then
  347. CMD="$CMD --cprofile $CPROFILE_OUTPUT"
  348. fi
  349. if [ -f "$MEMORY_PROFILE_OUTPUT" ]; then
  350. CMD="$CMD --memory $MEMORY_PROFILE_OUTPUT"
  351. fi
  352. if [ -f "$FLAMEGRAPH_OUTPUT" ]; then
  353. CMD="$CMD --flamegraph $FLAMEGRAPH_OUTPUT"
  354. fi
  355. if [ -f "$PSUTIL_OUTPUT" ]; then
  356. CMD="$CMD --psutil $PSUTIL_OUTPUT"
  357. fi
  358. CMD="$CMD --output $INTEGRATED_REPORT"
  359. # 执行整合脚本
  360. log_info "执行整合命令: $CMD"
  361. if eval "$CMD"; then
  362. log_success "所有分析结果已整合至 $INTEGRATED_REPORT"
  363. else
  364. log_error "整合过程出错"
  365. # 检查pandas是否存在
  366. if ! python -c "import pandas" &> /dev/null; then
  367. log_warning "未找到pandas,尝试安装..."
  368. if pip3 install pandas -i https://pypi.tuna.tsinghua.edu.cn/simple; then
  369. log_success "pandas 安装成功"
  370. else
  371. log_error "pandas 安装失败"
  372. return 1
  373. fi
  374. # 尝试再次执行命令
  375. if eval "$CMD"; then
  376. log_success "所有分析结果已整合至 $INTEGRATED_REPORT"
  377. else
  378. log_error "无法执行整合命令"
  379. return 1
  380. fi
  381. else
  382. return 1
  383. fi
  384. fi
  385. # 绘制融合结果图
  386. if [ -f "$PLOT_SCRIPT" ]; then
  387. log_info "绘制融合结果图"
  388. if python "$PLOT_SCRIPT" --input "$INTEGRATED_REPORT" --output "$INTEGRATED_PLOT"; then
  389. log_success "融合结果图已保存至 $INTEGRATED_PLOT"
  390. else
  391. log_error "无法绘制融合结果图"
  392. return 1
  393. fi
  394. else
  395. log_warning "未找到绘制脚本 $PLOT_SCRIPT"
  396. return 1
  397. fi
  398. fi
  399. return 0
  400. }
  401. # 主函数
  402. main() {
  403. # 显示标题
  404. show_heade
  405. # 检查环境
  406. if ! check_env;then
  407. log_error "请检查环境是否满足要求."
  408. fi
  409. # 检查脚本是否存在
  410. if ! check_script_exists; then
  411. log_error "请检查测试脚本是否存在."
  412. exit 1
  413. fi
  414. # 创建结果保存目录
  415. mk_dir
  416. # 执行各项分析
  417. run_cprofile # cProfile 算法性能分析,统计主线程的函数调用次数和运行时间
  418. run_py_spy # py-spy 火焰图
  419. run_memory_profiler # 内存使用情况分析
  420. run_psutil # 使用psutil监控进程资源使用情况
  421. show_summary # 显示结果摘要
  422. integrate_all_results # 整合所有结果
  423. # 自动打开结果文件
  424. open_svg_file
  425. open_prof_file
  426. }
  427. # 执行主函数
  428. main "$@"