Download gpu_utils/gpu_stress.py from hulehule/pandada: direct link, hf CLI and curl.
- Browser
- Download file 18.3 kB
-
https://huggingface.co/hulehule/pandada/resolve/main/gpu_utils/gpu_stress.py
- Command line
-
hf download hf://hulehule/pandada/gpu_utils/gpu_stress.py
-
curl -L -o gpu_stress.py https://huggingface.co/hulehule/pandada/resolve/main/gpu_utils/gpu_stress.py
18.3 kB
| ''' | |
| 运行: | |
| python gpu_utils/gpu_stress.py --target_utilization 0.8 --duration 4 --num_gpus 8 | |
| ''' | |
| import torch | |
| import torch.nn as nn | |
| import torch.nn.functional as F | |
| import time | |
| import threading | |
| import argparse | |
| import numpy as np | |
| from typing import List, Dict | |
| import psutil | |
| import os | |
| from concurrent.futures import ThreadPoolExecutor | |
| import multiprocessing as mp | |
| class H100StressTest: | |
| def __init__(self, target_utilization: float = 0.5, num_gpus: int = None): | |
| self.target_utilization = target_utilization | |
| self.running = False | |
| self.stats = { | |
| 'operations': 0, | |
| 'start_time': None, | |
| 'peak_memory': 0, | |
| 'avg_utilization': 0 | |
| } | |
| # 检查GPU可用性并确定使用的GPU数量 | |
| if torch.cuda.is_available(): | |
| available_gpus = torch.cuda.device_count() | |
| if num_gpus is None: | |
| self.num_gpus = available_gpus | |
| else: | |
| self.num_gpus = min(num_gpus, available_gpus) | |
| self.devices = [torch.device(f"cuda:{i}") for i in range(self.num_gpus)] | |
| print(f"检测到 {available_gpus} 个GPU,使用 {self.num_gpus} 个GPU") | |
| for i in range(self.num_gpus): | |
| print(f"GPU {i}: {torch.cuda.get_device_name(i)}") | |
| print(f"GPU {i} 内存: {torch.cuda.get_device_properties(i).total_memory / 1024**3:.1f} GB") | |
| print(f"GPU {i} 计算能力: {torch.cuda.get_device_properties(i).major}.{torch.cuda.get_device_properties(i).minor}") | |
| else: | |
| raise RuntimeError("CUDA不可用,无法进行GPU压力测试") | |
| def create_large_transformer_model(self, hidden_size: int = 4096, num_layers: int = 8, device: torch.device = None) -> nn.Module: | |
| """创建大型Transformer模型以充分利用H100""" | |
| class LargeTransformer(nn.Module): | |
| def __init__(self, hidden_size, num_layers): | |
| super().__init__() | |
| self.hidden_size = hidden_size | |
| self.num_layers = num_layers | |
| # 多层Transformer编码器 | |
| encoder_layer = nn.TransformerEncoderLayer( | |
| d_model=hidden_size, | |
| nhead=16, # 多头注意力 | |
| dim_feedforward=hidden_size * 4, # 前馈网络 | |
| dropout=0.1, | |
| batch_first=True, | |
| norm_first=True | |
| ) | |
| self.transformer = nn.TransformerEncoder(encoder_layer, num_layers=num_layers) | |
| # 额外的线性层 | |
| self.input_projection = nn.Linear(hidden_size, hidden_size) | |
| self.output_projection = nn.Linear(hidden_size, hidden_size) | |
| self.layer_norm = nn.LayerNorm(hidden_size) | |
| def forward(self, x): | |
| x = self.input_projection(x) | |
| x = self.transformer(x) | |
| x = self.layer_norm(x) | |
| x = self.output_projection(x) | |
| return x | |
| if device is None: | |
| device = self.devices[0] | |
| model = LargeTransformer(hidden_size, num_layers).to(device) | |
| return model.half() # 使用半精度以节省内存并提高性能 | |
| def create_convolutional_model(self, device: torch.device = None) -> nn.Module: | |
| """创建卷积神经网络模型""" | |
| class ConvNet(nn.Module): | |
| def __init__(self): | |
| super().__init__() | |
| self.conv1 = nn.Conv2d(3, 64, 3, padding=1) | |
| self.conv2 = nn.Conv2d(64, 128, 3, padding=1) | |
| self.conv3 = nn.Conv2d(128, 256, 3, padding=1) | |
| self.conv4 = nn.Conv2d(256, 512, 3, padding=1) | |
| self.conv5 = nn.Conv2d(512, 1024, 3, padding=1) | |
| self.pool = nn.MaxPool2d(2, 2) | |
| self.adaptive_pool = nn.AdaptiveAvgPool2d((1, 1)) | |
| self.fc1 = nn.Linear(1024, 2048) | |
| self.fc2 = nn.Linear(2048, 4096) | |
| self.fc3 = nn.Linear(4096, 2048) | |
| self.fc4 = nn.Linear(2048, 1024) | |
| self.dropout = nn.Dropout(0.1) | |
| def forward(self, x): | |
| x = F.relu(self.conv1(x)) | |
| x = self.pool(x) | |
| x = F.relu(self.conv2(x)) | |
| x = self.pool(x) | |
| x = F.relu(self.conv3(x)) | |
| x = self.pool(x) | |
| x = F.relu(self.conv4(x)) | |
| x = self.pool(x) | |
| x = F.relu(self.conv5(x)) | |
| x = self.adaptive_pool(x) | |
| x = x.view(x.size(0), -1) | |
| x = F.relu(self.fc1(x)) | |
| x = self.dropout(x) | |
| x = F.relu(self.fc2(x)) | |
| x = self.dropout(x) | |
| x = F.relu(self.fc3(x)) | |
| x = self.dropout(x) | |
| x = self.fc4(x) | |
| return x | |
| if device is None: | |
| device = self.devices[0] | |
| model = ConvNet().to(device) | |
| return model.half() | |
| def matrix_operations_intensive(self, size: int = 8192, duration: float = 10.0, device: torch.device = None): | |
| """高强度矩阵运算""" | |
| if device is None: | |
| device = self.devices[0] | |
| # 创建大矩阵 | |
| a = torch.randn(size, size, device=device, dtype=torch.float16) | |
| b = torch.randn(size, size, device=device, dtype=torch.float16) | |
| c = torch.randn(size, size, device=device, dtype=torch.float16) | |
| start_time = time.time() | |
| operations = 0 | |
| while time.time() - start_time < duration: | |
| # 矩阵乘法链 | |
| result = torch.mm(a, b) | |
| result = torch.mm(result, c) | |
| result = torch.mm(result, a.transpose(0, 1)) | |
| # 矩阵求逆(小矩阵) | |
| if size <= 2048: | |
| try: | |
| inv_result = torch.inverse(result[:min(1024, size), :min(1024, size)]) | |
| result = torch.mm(result, inv_result) | |
| except: | |
| pass | |
| # 矩阵加法 | |
| result = result + b | |
| result = result + c | |
| # 矩阵转置 | |
| result = result.transpose(0, 1) | |
| operations += 1 | |
| # 每100次操作同步一次 | |
| if operations % 100 == 0: | |
| torch.cuda.synchronize(device) | |
| return operations | |
| def transformer_training_loop(self, model, optimizer, criterion, batch_size: int = 16, seq_length: int = 2048, device: torch.device = None): | |
| """Transformer训练循环""" | |
| if device is None: | |
| device = self.devices[0] | |
| hidden_size = model.hidden_size | |
| # 创建输入数据 | |
| x = torch.randn(batch_size, seq_length, hidden_size, device=device, dtype=torch.float16) | |
| y = torch.randn(batch_size, seq_length, hidden_size, device=device, dtype=torch.float16) | |
| # 前向传播 | |
| outputs = model(x) | |
| loss = criterion(outputs, y) | |
| # 反向传播 | |
| optimizer.zero_grad() | |
| loss.backward() | |
| optimizer.step() | |
| return loss.item() | |
| def conv_training_loop(self, model, optimizer, criterion, batch_size: int = 32, device: torch.device = None): | |
| """卷积网络训练循环""" | |
| if device is None: | |
| device = self.devices[0] | |
| # 创建输入数据 | |
| x = torch.randn(batch_size, 3, 224, 224, device=device, dtype=torch.float16) | |
| y = torch.randn(batch_size, 1024, device=device, dtype=torch.float16) | |
| # 前向传播 | |
| outputs = model(x) | |
| loss = criterion(outputs, y) | |
| # 反向传播 | |
| optimizer.zero_grad() | |
| loss.backward() | |
| optimizer.step() | |
| return loss.item() | |
| def monitor_gpu_stats(self): | |
| """监控所有GPU统计信息""" | |
| while self.running: | |
| try: | |
| total_allocated = 0 | |
| total_cached = 0 | |
| gpu_stats = [] | |
| # 获取所有GPU的内存使用情况 | |
| for i in range(self.num_gpus): | |
| allocated = torch.cuda.memory_allocated(i) / 1024**3 | |
| cached = torch.cuda.memory_reserved(i) / 1024**3 | |
| total_allocated += allocated | |
| total_cached += cached | |
| gpu_stats.append((allocated, cached)) | |
| # 更新峰值内存 | |
| if total_allocated > self.stats['peak_memory']: | |
| self.stats['peak_memory'] = total_allocated | |
| # 每10秒打印一次状态 | |
| if self.stats['start_time'] and int(time.time() - self.stats['start_time']) % 10 == 0: | |
| elapsed = time.time() - self.stats['start_time'] | |
| ops_per_sec = self.stats['operations'] / elapsed if elapsed > 0 else 0 | |
| print(f"[{elapsed:.0f}s] 操作数: {self.stats['operations']}, " | |
| f"操作/秒: {ops_per_sec:.1f}") | |
| print(f"总GPU内存: {total_allocated:.2f}GB/{total_cached:.2f}GB, " | |
| f"峰值内存: {self.stats['peak_memory']:.2f}GB") | |
| # 显示每个GPU的详细状态 | |
| for i, (allocated, cached) in enumerate(gpu_stats): | |
| print(f" GPU {i}: {allocated:.2f}GB/{cached:.2f}GB") | |
| time.sleep(1) | |
| except Exception as e: | |
| print(f"监控错误: {e}") | |
| time.sleep(5) | |
| def gpu_worker(self, gpu_id: int, duration_seconds: int, shared_stats: dict, stats_lock: threading.Lock): | |
| """单个GPU的工作线程""" | |
| device = self.devices[gpu_id] | |
| print(f"GPU {gpu_id} 开始工作...") | |
| # 为每个GPU创建独立的模型 | |
| transformer_model = self.create_large_transformer_model( | |
| hidden_size=4096, num_layers=12, device=device | |
| ) | |
| transformer_optimizer = torch.optim.AdamW( | |
| transformer_model.parameters(), lr=1e-4, weight_decay=0.01 | |
| ) | |
| transformer_criterion = nn.MSELoss() | |
| conv_model = self.create_convolutional_model(device=device) | |
| conv_optimizer = torch.optim.AdamW( | |
| conv_model.parameters(), lr=1e-4, weight_decay=0.01 | |
| ) | |
| conv_criterion = nn.MSELoss() | |
| # 预热GPU | |
| for _ in range(5): | |
| self.matrix_operations_intensive(size=4096, duration=1.0, device=device) | |
| start_time = time.time() | |
| local_operations = 0 | |
| cycle_count = 0 | |
| try: | |
| while time.time() - start_time < duration_seconds: | |
| cycle_count += 1 | |
| current_time = time.time() - start_time | |
| # 每30秒轮换不同类型的操作 | |
| if int(current_time) % 30 < 10: | |
| # 矩阵运算 | |
| ops = self.matrix_operations_intensive(size=8192, duration=8, device=device) | |
| local_operations += ops | |
| elif int(current_time) % 30 < 20: | |
| # Transformer训练 | |
| for _ in range(5): | |
| loss = self.transformer_training_loop( | |
| transformer_model, transformer_optimizer, transformer_criterion, | |
| batch_size=8, seq_length=2048, device=device | |
| ) | |
| local_operations += 1 | |
| else: | |
| # 卷积网络训练 | |
| for _ in range(10): | |
| loss = self.conv_training_loop( | |
| conv_model, conv_optimizer, conv_criterion, | |
| batch_size=16, device=device | |
| ) | |
| local_operations += 1 | |
| # 每100个周期清理一次缓存 | |
| if cycle_count % 100 == 0: | |
| torch.cuda.empty_cache() | |
| # 短暂休息以控制利用率 | |
| time.sleep(0.1) | |
| except Exception as e: | |
| print(f"GPU {gpu_id} 发生错误: {e}") | |
| finally: | |
| # 更新共享统计信息 | |
| with stats_lock: | |
| shared_stats['operations'] += local_operations | |
| print(f"GPU {gpu_id} 完成工作,操作数: {local_operations}") | |
| def run_intensive_stress_test(self, duration_hours: float = 2.0): | |
| """运行多GPU高强度压力测试""" | |
| duration_seconds = int(duration_hours * 3600) | |
| print(f"开始多GPU高强度压力测试") | |
| print(f"使用GPU数量: {self.num_gpus}") | |
| print(f"目标利用率: {self.target_utilization*100:.1f}%") | |
| print(f"持续时间: {duration_hours:.1f}小时 ({duration_seconds}秒)") | |
| # 启动监控线程 | |
| self.running = True | |
| self.stats['start_time'] = time.time() | |
| monitor_thread = threading.Thread(target=self.monitor_gpu_stats) | |
| monitor_thread.daemon = True | |
| monitor_thread.start() | |
| # 创建共享统计信息和锁 | |
| shared_stats = {'operations': 0} | |
| stats_lock = threading.Lock() | |
| # 使用线程池并行运行所有GPU | |
| with ThreadPoolExecutor(max_workers=self.num_gpus) as executor: | |
| # 为每个GPU提交任务 | |
| futures = [] | |
| for gpu_id in range(self.num_gpus): | |
| future = executor.submit( | |
| self.gpu_worker, gpu_id, duration_seconds, shared_stats, stats_lock | |
| ) | |
| futures.append(future) | |
| try: | |
| # 等待所有任务完成 | |
| for future in futures: | |
| future.result() | |
| except KeyboardInterrupt: | |
| print("\n用户中断测试") | |
| # 取消所有未完成的任务 | |
| for future in futures: | |
| future.cancel() | |
| self.running = False | |
| elapsed = time.time() - self.stats['start_time'] | |
| print(f"\n多GPU压力测试完成!") | |
| print(f"总运行时间: {elapsed:.1f}秒 ({elapsed/3600:.2f}小时)") | |
| print(f"总操作数: {shared_stats['operations']}") | |
| print(f"平均操作/秒: {shared_stats['operations']/elapsed:.1f}") | |
| print(f"峰值GPU内存使用: {self.stats['peak_memory']:.2f}GB") | |
| def get_gpu_info(self): | |
| """获取所有GPU的详细信息""" | |
| if torch.cuda.is_available(): | |
| print("=== 多GPU 详细信息 ===") | |
| print(f"GPU数量: {torch.cuda.device_count()}") | |
| print(f"使用GPU数量: {self.num_gpus}") | |
| total_memory = 0 | |
| for i in range(self.num_gpus): | |
| print(f"\n--- GPU {i} ---") | |
| print(f"GPU名称: {torch.cuda.get_device_name(i)}") | |
| props = torch.cuda.get_device_properties(i) | |
| gpu_memory = props.total_memory / 1024**3 | |
| total_memory += gpu_memory | |
| print(f"总内存: {gpu_memory:.1f} GB") | |
| print(f"多处理器数量: {props.multi_processor_count}") | |
| print(f"计算能力: {props.major}.{props.minor}") | |
| # 安全获取可能不存在的属性 | |
| try: | |
| print(f"最大线程数/块: {props.max_threads_per_block}") | |
| except AttributeError: | |
| print("最大线程数/块: 信息不可用") | |
| try: | |
| print(f"最大网格尺寸: {props.max_grid_size}") | |
| except AttributeError: | |
| print("最大网格尺寸: 信息不可用") | |
| try: | |
| print(f"共享内存/块: {props.shared_memory_per_block / 1024:.0f} KB") | |
| except AttributeError: | |
| print("共享内存/块: 信息不可用") | |
| # 获取当前内存使用情况 | |
| allocated = torch.cuda.memory_allocated(i) / 1024**3 | |
| cached = torch.cuda.memory_reserved(i) / 1024**3 | |
| print(f"已分配内存: {allocated:.2f} GB") | |
| print(f"缓存内存: {cached:.2f} GB") | |
| print(f"可用内存: {gpu_memory - cached:.2f} GB") | |
| print(f"\n总GPU内存: {total_memory:.1f} GB") | |
| else: | |
| print("CUDA不可用") | |
| def main(): | |
| parser = argparse.ArgumentParser(description='多GPU高强度压力测试工具') | |
| parser.add_argument('--duration', type=float, default=2.0, | |
| help='测试持续时间(小时)') | |
| parser.add_argument('--target_utilization', type=float, default=0.5, | |
| help='目标GPU利用率(0-1)') | |
| parser.add_argument('--num_gpus', type=int, default=None, | |
| help='使用的GPU数量(默认使用所有可用GPU)') | |
| parser.add_argument('--info_only', action='store_true', | |
| help='只显示GPU信息,不进行测试') | |
| args = parser.parse_args() | |
| try: | |
| # 创建压力测试实例 | |
| stress_test = H100StressTest( | |
| target_utilization=args.target_utilization, | |
| num_gpus=args.num_gpus | |
| ) | |
| # 显示GPU信息 | |
| stress_test.get_gpu_info() | |
| if args.info_only: | |
| return | |
| # 运行压力测试 | |
| stress_test.run_intensive_stress_test(duration_hours=args.duration) | |
| except Exception as e: | |
| print(f"错误: {e}") | |
| return 1 | |
| if __name__ == "__main__": | |
| exit(main()) | |