MSA-Code / src /utils /gpu_monitor.py
Anoy123423123's picture
Anonymous code release: MSA inference and evaluation
5e27996 verified
Raw
History Blame Contribute Delete
12.9 kB
import pynvml
import time
import threading
from datetime import datetime
import sys
import os
class GPUMemoryMonitor:
def __init__(self, gpu_index=0, interval=1.0, unit='GB'):
"""
初始化GPU显存监控器
Args:
gpu_index: 要监控的GPU索引,默认0
interval: 监控间隔时间(秒),默认1秒
unit: 返回的单位,支持 'MB' 或 'GB',默认'GB'
"""
self.gpu_index = gpu_index
self.interval = interval
self.unit = unit.upper()
# 验证单位参数
if self.unit not in ['MB', 'GB']:
raise ValueError("单位必须是 'MB' 或 'GB'")
# 监控相关状态
self.monitor_thread = None
self._running = False
self._lock = threading.Lock()
self.peak_memory_usage = 0 # 峰值显存使用量
self.start_time = None
self.stop_time = None
# 初始化NVML
try:
pynvml.nvmlInit()
self.device_count = pynvml.nvmlDeviceGetCount()
if self.gpu_index >= self.device_count:
raise ValueError(f"GPU索引 {self.gpu_index} 超出范围,系统只有 {self.device_count} 个GPU")
self.handle = pynvml.nvmlDeviceGetHandleByIndex(self.gpu_index)
# 获取GPU名称
self.gpu_name = pynvml.nvmlDeviceGetName(self.handle)
except Exception as e:
print(f"初始化NVML失败: {e}")
if 'pynvml' in sys.modules:
pynvml.nvmlShutdown()
raise
def _get_memory_info(self):
"""获取当前GPU显存信息"""
try:
mem_info = pynvml.nvmlDeviceGetMemoryInfo(self.handle)
return mem_info
except Exception as e:
print(f"获取GPU {self.gpu_index} 显存信息失败: {e}")
return None
def _convert_units(self, bytes_value):
"""转换字节为指定单位"""
if self.unit == 'MB':
return bytes_value / (1024 * 1024)
else: # GB
return bytes_value / (1024 * 1024 * 1024)
def _get_current_usage(self):
"""获取当前显存使用量(按指定单位)"""
mem_info = self._get_memory_info()
if mem_info:
return self._convert_units(mem_info.used)
return 0
def _monitor_loop(self):
"""监控循环,运行在单独线程中"""
# print(f"开始监控 GPU {self.gpu_index} ({self.gpu_name})")
# print(f"监控间隔: {self.interval}秒")
# print(f"单位: {self.unit}")
# print("按回车键停止监控...\n")
try:
while self._running:
# 获取当前显存使用量
current_usage = self._get_current_usage()
# 更新峰值
with self._lock:
if current_usage > self.peak_memory_usage:
self.peak_memory_usage = current_usage
# 等待下一个监控周期
time.sleep(self.interval)
except Exception as e:
print(f"监控线程出错: {e}")
finally:
# print(f"GPU {self.gpu_index} 监控线程结束")
pass
def start(self):
"""
启动显存监控
Returns:
bool: 是否成功启动
"""
if self._running:
# print(f"GPU {self.gpu_index} 监控已经在运行中")
return False
try:
# 重置峰值数据
with self._lock:
self.peak_memory_usage = 0
# 记录开始时间
self.start_time = datetime.now()
# 启动监控线程
self._running = True
self.monitor_thread = threading.Thread(target=self._monitor_loop)
self.monitor_thread.daemon = True
self.monitor_thread.start()
# 验证线程已启动
time.sleep(0.1)
if self.monitor_thread.is_alive():
# print(f"GPU {self.gpu_index} 监控已启动")
return True
else:
self._running = False
# print(f"GPU {self.gpu_index} 监控线程启动失败")
return False
except Exception as e:
print(f"启动GPU {self.gpu_index} 监控失败: {e}")
self._running = False
return False
def stop(self, verbose=False):
"""
停止显存监控并返回峰值显存使用量
Args:
verbose: 是否打印详细信息
Returns:
float: 峰值显存使用量(单位:GB或MB)
"""
if not self._running:
if verbose:
print(f"GPU {self.gpu_index} 监控未在运行")
return 0.0
try:
# 停止监控线程
self._running = False
self.stop_time = datetime.now()
# 等待线程结束(最多等待2秒)
if self.monitor_thread and self.monitor_thread.is_alive():
self.monitor_thread.join(timeout=2.0)
# 获取最终峰值
peak_usage = 0.0
with self._lock:
peak_usage = self.peak_memory_usage
if verbose:
duration = (self.stop_time - self.start_time).total_seconds()
print(f"\n{'='*50}")
print(f"GPU {self.gpu_index} ({self.gpu_name}) 监控结果")
print(f"{'='*50}")
print(f"开始时间: {self.start_time.strftime('%Y-%m-%d %H:%M:%S')}")
print(f"结束时间: {self.stop_time.strftime('%Y-%m-%d %H:%M:%S')}")
print(f"监控时长: {duration:.1f} 秒")
print(f"峰值显存使用量: {peak_usage:.3f} {self.unit}")
# 获取当前显存信息作为对比
mem_info = self._get_memory_info()
if mem_info:
total = self._convert_units(mem_info.total)
current = self._convert_units(mem_info.used)
print(f"当前显存使用: {current:.3f} / {total:.3f} {self.unit}")
print(f"峰值占比: {(peak_usage/total*100):.1f}%")
print(f"{'='*50}")
return peak_usage
except Exception as e:
print(f"停止GPU {self.gpu_index} 监控时出错: {e}")
return 0.0
def get_current_usage(self):
"""获取当前显存使用量(不停止监控)"""
return self._get_current_usage()
def get_peak_usage(self):
"""获取当前记录的峰值显存使用量(不停止监控)"""
with self._lock:
return self.peak_memory_usage
def is_running(self):
"""检查监控是否在运行"""
return self._running
def __del__(self):
"""析构函数,确保清理资源"""
if self._running:
self.stop(verbose=False)
try:
pynvml.nvmlShutdown()
except:
pass
# 使用示例函数
def monitor_gpu_memory_example():
"""使用示例"""
print("GPU显存监控示例")
print("=" * 50)
try:
# 创建监控器
monitor = GPUMemoryMonitor(
gpu_index=0, # 监控第一个GPU
interval=0.5, # 每0.5秒检查一次
unit='GB' # 使用GB作为单位
)
# 启动监控
if monitor.start():
# 这里可以运行你的GPU任务
print("\n现在开始运行你的GPU任务...")
print("监控正在后台进行")
# 模拟一些工作(在实际使用中,这里应该是你的GPU任务)
print("按回车键停止监控并获取峰值显存用量...")
input() # 等待用户按回车
# 停止监控并获取结果
peak_memory_gb = monitor.stop()
print(f"\n监控完成!峰值显存用量: {peak_memory_gb:.3f} GB")
except Exception as e:
print(f"错误: {e}")
# 多GPU监控示例
class MultiGPUMonitor:
"""多GPU监控器"""
def __init__(self, gpu_indices=None, interval=1.0, unit='GB'):
"""
初始化多GPU监控器
Args:
gpu_indices: 要监控的GPU索引列表,None表示监控所有GPU
interval: 监控间隔
unit: 返回的单位
"""
pynvml.nvmlInit()
device_count = pynvml.nvmlDeviceGetCount()
if gpu_indices is None:
gpu_indices = list(range(device_count))
self.monitors = []
for idx in gpu_indices:
if idx < device_count:
monitor = GPUMemoryMonitor(gpu_index=idx, interval=interval, unit=unit)
self.monitors.append(monitor)
else:
print(f"警告: GPU索引 {idx} 不存在,跳过")
def start_all(self):
"""启动所有GPU监控"""
results = []
for monitor in self.monitors:
success = monitor.start()
results.append((monitor.gpu_index, success))
return results
def stop_all(self):
"""停止所有GPU监控并返回结果"""
results = {}
max_peak = 0.0
for monitor in self.monitors:
peak = monitor.stop()
results[monitor.gpu_index] = {
'name': monitor.gpu_name,
'peak_memory': peak,
'unit': monitor.unit
}
if peak > max_peak:
max_peak = peak
return results, max_peak
def stop_all_and_get_max(self):
"""停止所有监控并返回最大峰值"""
results = self.stop_all()
if not results:
return 0.0
max_peak = max(item['peak_memory'] for item in results.values())
return max_peak
# 快速使用函数
def quick_monitor(gpu_index=0, interval=0.5, unit='GB', wait_for_input=True):
"""
快速启动监控的便捷函数
Args:
gpu_index: GPU索引
interval: 监控间隔
unit: 单位
wait_for_input: 是否等待用户输入
Returns:
float: 峰值显存使用量
"""
monitor = GPUMemoryMonitor(gpu_index=gpu_index, interval=interval, unit=unit)
try:
if monitor.start():
if wait_for_input:
print("按回车键停止监控...")
input()
else:
# 如果不等待用户输入,这里可以设置其他停止条件
# 例如:监控特定时间或直到某个条件满足
print("监控已启动,将在后台运行")
print("调用 monitor.stop() 来停止并获取结果")
return monitor
return None
except Exception as e:
print(f"监控失败: {e}")
return None
# 测试代码
if __name__ == "__main__":
# # 示例1: 基本使用
# print("示例1: 基本使用")
# monitor = GPUMemoryMonitor(gpu_index=0, interval=0.5, unit='GB')
# monitor.start()
# # 模拟一些GPU工作
# print("模拟GPU工作...")
# time.sleep(3)
# # 停止并获取结果
# peak = monitor.stop()
# print(f"峰值显存: {peak:.3f} GB\n")
# # 示例2: 使用便捷函数
# print("示例2: 使用便捷函数")
# result = quick_monitor(gpu_index=0, interval=0.2, unit='MB')
# if result:
# # 这里 result 是 monitor 对象
# time.sleep(2)
# peak = result.stop()
# print(f"峰值显存: {peak:.3f} MB")
# 示例3: 多GPU监控
print("\n示例3: 多GPU监控")
try:
pynvml.nvmlInit()
device_count = pynvml.nvmlDeviceGetCount()
print(f"系统中有 {device_count} 个GPU")
if device_count > 1:
# multi_monitor = MultiGPUMonitor(gpu_indices=[0, 1], interval=0.5, unit='GB')
multi_monitor = MultiGPUMonitor(interval=5, unit='GB')
multi_monitor.start_all()
time.sleep(2)
results, max_peak = multi_monitor.stop_all()
for gpu_idx, data in results.items():
print(f"GPU {gpu_idx} ({data['name']}): {data['peak_memory']:.3f} {data['unit']}")
print(f"最大峰值显存使用量: {max_peak:.3f} GB")
except:
pass