Просмотр исходного кода

增加命令行传参启动测试脚本,增加gpu统计的支持

jiyuhang 3 месяцев назад
Родитель
Сommit
954e361cf9
5 измененных файлов с 128 добавлено и 55 удалено
  1. 9 6
      integrate_results.py
  2. 51 42
      main.sh
  3. 61 5
      monitor_util.py
  4. 3 2
      plot_offline.py
  5. 4 0
      test/test_script.py

+ 9 - 6
integrate_results.py

@@ -18,11 +18,14 @@ def parse_cprofile_data(cprofile_file, top_K=20):
         # 提取前top_k个最耗时的函数
         top_functions = []
         for func, (cc, nc, tt, ct, callers) in sorted_functions[:top_K]:
+            average_time = tt / cc if cc > 0 else 0
             top_functions.append({
                 'function': str(func),
                 'call_count': cc,
+
+                'cumulative_time': ct,
                 'total_time': tt,
-                'cumulative_time': ct
+                'average_time_per_call': average_time
             })
 
         return {
@@ -120,11 +123,11 @@ def integrate_results(cprofile_file, memory_file, flamegraph_file, psutil_file,
 
 def main():
     parser = argparse.ArgumentParser(description='整合Python性能分析结果')
-    parser.add_argument('--cprofile', default='./cprofile_output.prof',help='cProfile输出文件路径')
-    parser.add_argument('--memory', default='./mprofile_output.dat', help='memory_profiler输出文件路径')
-    parser.add_argument('--flamegraph',default='./pyspy_output.svg', help='py-spy火焰图文件路径')
-    parser.add_argument('--psutil',default='./output.csv', help='psutil监控数据文件路径')
-    parser.add_argument('--output', default='integrated_report.json', help='整合报告输出路径')
+    parser.add_argument('--cprofile', default='./results/cprofile_output.prof',help='cProfile输出文件路径')
+    parser.add_argument('--memory', default='./results/mprofile_output.dat', help='memory_profiler输出文件路径')
+    parser.add_argument('--flamegraph',default='./results/pyspy_output.svg', help='py-spy火焰图文件路径')
+    parser.add_argument('--psutil',default='./results/psutil_output.csv', help='psutil监控数据文件路径')
+    parser.add_argument('--output', default='./results/integrated_report.json', help='整合报告输出路径')
 
     args = parser.parse_args()
 

+ 51 - 42
main.sh

@@ -137,19 +137,6 @@ run_cprofile() {
 # 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
     # 将之前的结果删除
     rm -rf "$MEMORY_PROFILE_OUTPUT"
     # 运行内存分析
@@ -175,23 +162,6 @@ run_memory_profiler() {
 # 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
     # 删除之前的结果
     rm -rf "$FLAMEGRAPH_OUTPUT"
     # 根据操作系统类型选择合适的py-spy参数,需要统计子线程和C拓展
@@ -225,17 +195,7 @@ run_py_spy() {
 # 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
+
   # 将之前的结果文件删除
   rm -rf "$PSUTIL_OUTPUT"
   # 在后台运行test_script.py并获取其PID
@@ -273,7 +233,52 @@ run_psutil() {
   
   log_success "资源监控完成,结果保存至 $PSUTIL_OUTPUT"
 }
+# 0.检查环境完备性
+check_env(){
+    # 检查是否安装了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
 
+    # 检查是否安装了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
+    # 检查是否安装了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
+    # 检查是否安装nvidia-ml-py
+    if ! python -c "import pynvml" &> /dev/null; then
+      log_warning "未找到 nvidia_ml_py ,尝试安装..."
+      if pip3 install nvidia-ml-py -i https://pypi.tuna.tsinghua.edu.cn/simple; then
+        log_success "nvidia_ml_py 安装成功"
+      else
+        log_error "nvidia_ml_py 安装失败"
+        return 1
+      fi
+    fi
+}
 # 自动打开SVG文件
 open_svg_file() {
     if [ -f "$FLAMEGRAPH_OUTPUT" ]; then
@@ -428,7 +433,11 @@ integrate_all_results(){
 # 主函数
 main() {
     # 显示标题
-    show_header
+    show_heade
+    # 检查环境
+    if ! check_env;then
+       log_error "请检查环境是否满足要求."
+    fi
     # 检查脚本是否存在
     if ! check_script_exists; then
        log_error "请检查测试脚本是否存在."

+ 61 - 5
monitor_util.py

@@ -1,12 +1,15 @@
+import os
 import psutil
 import time
 from datetime import datetime
-import os
 import json
 import csv
-
+from numba import cuda
+import numpy as np
+import pynvml
 class Monitor:
     def __init__(self, pid:int, alias:str=''):
+        self.cuda_tensor = cuda.to_device(np.arange(10000).astype(np.float32))
         self.pid = pid
         self.process = None
         self.curr_disk_io_counter = None  # 磁盘读写计数器
@@ -108,7 +111,56 @@ class Monitor:
         self.usage['sys_net_recv'] = f'{sys_net_recv_speed:.2f} MB/s'
         self.curr_system_net_io_counter = new_net_io_counter
 
-
+    def get_gpu_usage_info(self):
+        try:
+            # 验证进程是否存在
+            if not psutil.pid_exists(self.pid): 
+                return
+            # 使用 nvidia-ml-py 监控 GPU
+            pynvml.nvmlInit()
+            device_count = pynvml.nvmlDeviceGetCount()
+            for i in range(device_count):
+                handle = pynvml.nvmlDeviceGetHandleByIndex(i)
+                processes = pynvml.nvmlDeviceGetComputeRunningProcesses(handle)
+                for process in processes:
+                    if process.pid == self.pid:
+                        # 获取进程 GPU 使用详情
+                        memory_mb = process.usedGpuMemory / (1024 * 1024) if process.usedGpuMemory else 0  # 显存 转换为MB
+                        try:
+                            gpu_util = pynvml.nvmlDeviceGetUtilizationRates(handle)  # GPU 使用详情
+                        except:
+                            gpu_util = None
+                        # 获取GPU温度
+                        try:
+                            temperature = pynvml.nvmlDeviceGetTemperature(handle, pynvml.NVML_TEMPERATURE_GPU)
+                        except:
+                            temperature = 0
+                        # 获取GPU功耗
+                        try:
+                            power_usage = pynvml.nvmlDeviceGetPowerUsage(handle)
+                            power_watts = power_usage / 1000.0 if power_usage else 0
+                        except:
+                            power_watts = 0
+                        device_id = f'gpu_{i}_'
+                        self.usage[device_id+'mem'] = memory_mb
+                        self.usage[device_id+'rate_gpu'] = gpu_util.gpu if gpu_util else 0
+                        # self.usage[device_id+'rate_mem'] = gpu_util.memory if gpu_util else 0
+                        self.usage[device_id+'temperature'] = temperature
+                        self.usage[device_id+'power'] = power_watts
+                        # print(f"GPU {i}:")
+                        # print(f"  内存使用: {memory_mb:.2f} MB")
+                        # print(f"  GPU 利用率: {gpu_util.gpu if gpu_util else 0}%")
+                        # print(f"  内存利用率: {gpu_util.memory if gpu_util else 0}%")
+                        # print(f"  GPU温度: {temperature}°C")
+                        # print(f"  GPU功耗: {power_watts:.2f}W")
+            pynvml.nvmlShutdown()
+        except psutil.NoSuchProcess as e:
+            print(f"进程 PID {self.pid} 不存在")
+        except Exception as e:
+            print(f"GPU监控出错: {e}")
+            # 添加详细的错误跟踪
+            import traceback
+            traceback.print_exc()
     def update(self, output_file=None):
         self.usage['now_time'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
         self.usage['status'] = self.process.status()
@@ -120,6 +172,8 @@ class Monitor:
         self.get_disk_io_info()
         # 刷新网络上下行情况
         self.get_net_io_info()
+        # 获取GPU使用情况
+        self.get_gpu_usage_info()
         if output_file:
             self.data_points.append(self.usage.copy())
             if len(self.data_points) >= self.max_count:  # 如果达到100个数据点,则保存到CSV文件中,并清空数据点
@@ -149,8 +203,10 @@ class Monitor:
 def main():
     import argparse
     parser = argparse.ArgumentParser(description='Py进程监视器')
-    parser.add_argument('--pid', '-p', type=int, required=True, help='请输入需要监控的进程id')
-    parser.add_argument('--output', '-o', type=str, required=True, help='请输入需要监控的进程id')
+    # parser.add_argument('--pid', '-p', type=int, required=True, help='请输入需要监控的进程id')
+    # parser.add_argument('--output', '-o', type=str, required=True, help='请输入需要监控的进程id')
+    parser.add_argument('--pid', '-p', type=int, default=os.getpid(), help='请输入需要监控的进程id')
+    parser.add_argument('--output', '-o', type=str, default='./results/psutil_output.csv', help='请输入输出文件路径')
     args = parser.parse_args()
     # 删除已经存在的结果
     if os.path.exists(args.output):

+ 3 - 2
plot_offline.py

@@ -114,12 +114,13 @@ def plot_cprofile_table(ax, cprofile_data):
         table_data.append([
             func_name_display,
             point['call_count'],
+            f"{point['cumulative_time']:.6f}",
             f"{point['total_time']:.6f}",
-            f"{point['cumulative_time']:.6f}"
+            f"{point['average_time_per_call']:.6f}"
         ])
     
     # 创建表格
-    columns = ['函数名称', '调用次数', '总时间(s)', '累计时间(s)']
+    columns = ['函数名称', '调用次数', '累计时间(s)', '总时间(s)', '平均时间(s)']
     table = ax.table(cellText=table_data, colLabels=columns, cellLoc='center', loc='center', 
                      bbox=[0, 0, 1, 1])
     table.auto_set_font_size(False)

+ 4 - 0
test/test_script.py

@@ -5,6 +5,10 @@
 
 import time
 import math
+from numba import cuda
+import numpy as np
+# 模拟一个CUDA张量
+cuda_tensor = cuda.to_device(np.arange(10000).astype(np.float32))
 
 def cpu_intensive_task():
     """模拟CPU密集型任务"""