重置GPU.ps1

<#PSScriptInfo
 
.VERSION 1.1.0
 
.GUID 9c750cc4-ecc4-4f74-876c-4331a1c820ce
 
.AUTHOR 埃博拉酱
 
.COMPANYNAME 无
 
.COPYRIGHT (c) 2026 埃博拉酱
 
.TAGS NVIDIA GPU Reset TDR PnP CUDA 显卡 重置
 
.LICENSEURI https://opensource.org/licenses/MIT
 
#>


<#
.SYNOPSIS
    重置指定序号的 NVIDIA GPU(无需重启整机);不指定参数时重置所有 GPU
 
.DESCRIPTION
    参考 https://juejin.cn/post/7663097122267414555
    通过 Windows PnP 接口禁用再启用目标 GPU,触发驱动重新初始化,
    清空 CUDA context、复位传感器、恢复时钟。
    不带任何参数运行时,自动枚举 nvidia-smi 中所有 GPU 并逐个重置。
 
.PARAMETER 序号
    GPU 在 nvidia-smi 中显示的序号(0, 1, 2, ...),可传多个,如 -序号 0,2。
    省略时重置所有 GPU。
 
.EXAMPLE
    .\重置GPU.ps1 -序号 1
    重置 GPU 1
 
.EXAMPLE
    .\重置GPU.ps1
    重置所有 GPU
 
.NOTES
    - 仅限未连接显示器的 GPU(Display Attached = No)
    - 重置会断开该 GPU 上所有 CUDA context,正在运行的计算任务将丢失
    - 需要管理员权限运行
#>


[CmdletBinding()]
param(
    [Parameter(Mandatory = $false, Position = 0, HelpMessage = "GPU 序号(nvidia-smi 中的 index),可传多个;省略时重置所有 GPU")]
    [int[]]$序号
)

$ErrorActionPreference = 'Stop'

# ========== 0. 检查管理员权限 ==========
$当前用户 = [Security.Principal.WindowsIdentity]::GetCurrent()
$主体 = New-Object Security.Principal.WindowsPrincipal($当前用户)
if (-not $主体.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
    Write-Error "请以管理员身份运行 PowerShell 后重试"
    exit 1
}

# ========== 1. 确定要重置的 GPU 序号列表 ==========
# 未指定任何参数时,枚举 nvidia-smi 中所有 GPU
if (-not $序号) {
    Write-Host "==> 未指定序号,枚举所有 GPU..." -ForegroundColor Cyan
    $序号 = @(nvidia-smi --query-gpu=index --format=csv,noheader |
        ForEach-Object { [int]$_.Trim() })
    if (-not $序号) {
        Write-Error "nvidia-smi 未检测到任何 GPU"
        exit 1
    }
    Write-Host " 共发现 $($序号.Count) 个 GPU: $($序号 -join ', ')"
}

# ========== 2. 重置单个 GPU 的核心逻辑 ==========
function Reset-SingleGpu {
    param([int]$Gpu序号)

    # --- 2.1 获取目标 GPU 的 PCI 总线号 ---
    Write-Host "`n==> 查询 GPU $Gpu序号 的 PCI 总线号..." -ForegroundColor Cyan
    $所有行 = nvidia-smi --query-gpu=index,pci.bus_id --format=csv,noheader
    $目标行 = $所有行 | Where-Object { [int](($_ -split ',')[0].Trim()) -eq $Gpu序号 }

    if (-not $目标行) {
        Write-Error "未找到序号为 $Gpu序号 的 GPU,请检查 nvidia-smi 输出"
        return
    }

    # bus_id 形如 00000000:36:00.0,取中间段十六进制转十进制
    $总线号十六进制 = (($目标行 -split ',')[1].Trim() -split ':')[1]
    $总线号 = [Convert]::ToInt32($总线号十六进制, 16)
    Write-Host " PCI 总线号: 0x$总线号十六进制 = $总线号 (十进制)"

    # --- 2.2 确认 GPU 未连接显示器 ---
    Write-Host "==> 检查显示器连接状态..." -ForegroundColor Cyan
    $显示状态 = nvidia-smi -q -i $Gpu序号 | Select-String -Pattern 'Display Attached|Display Active'
    $已连接显示器 = ($显示状态 -match 'Display Attached\s+:\s+Yes')

    if ($已连接显示器) {
        Write-Warning "GPU $Gpu序号 连接了显示器,禁用会导致黑屏,已跳过"
        return
    }
    Write-Host " Display Attached: No (可安全重置)"

    # --- 2.3 通过 PCI 总线号匹配 PnP InstanceId ---
    Write-Host "==> 匹配 PnP InstanceId..." -ForegroundColor Cyan
    $显示设备 = Get-PnpDevice -Class 'Display' -Status OK, Unknown |
        Where-Object { $_.FriendlyName -like '*NVIDIA*' }

    $实例ID = $null
    foreach ($设备 in $显示设备) {
        $位置信息 = Get-PnpDeviceProperty -InstanceId $设备.InstanceId `
            -KeyName 'DEVPKEY_Device_LocationInfo' -ErrorAction SilentlyContinue
        # 位置信息形如 "PCI bus 54, device 0, function 0" 或 "PCI 总线 54、设备 0、功能 0"
        if ($位置信息.Data -match "PCI (bus|总线)\s*$总线号[、,]") {
            $实例ID = $设备.InstanceId
            break
        }
    }

    if (-not $实例ID) {
        Write-Error "未找到 PCI 总线 $总线号 对应的 NVIDIA PnP 设备,已跳过 GPU $Gpu序号"
        return
    }
    Write-Host " InstanceId: $实例ID"

    # --- 2.4 显示重置前状态 ---
    Write-Host "`n===== GPU $Gpu序号 重置前状态 =====" -ForegroundColor Yellow
    nvidia-smi --query-gpu=index,utilization.gpu,power.draw,temperature.gpu,pstate,memory.used `
        --format=csv -i $Gpu序号

    # --- 2.5 禁用 → 等待 → 启用 ---
    Write-Host "`n==> 正在禁用 GPU $Gpu序号 ..." -ForegroundColor Cyan
    Disable-PnpDevice -InstanceId $实例ID -Confirm:$false

    Write-Host "==> 等待 3 秒..." -ForegroundColor Cyan
    Start-Sleep -Seconds 3

    Write-Host "==> 正在重新启用 GPU $Gpu序号 ..." -ForegroundColor Cyan
    Enable-PnpDevice -InstanceId $实例ID -Confirm:$false

    # 等待驱动初始化完成
    Start-Sleep -Seconds 2

    # --- 2.6 验证重置结果 ---
    Write-Host "`n===== GPU $Gpu序号 重置后状态 =====" -ForegroundColor Green
    nvidia-smi --query-gpu=index,utilization.gpu,power.draw,temperature.gpu,pstate,memory.used `
        --format=csv -i $Gpu序号

    Write-Host "`nGPU $Gpu序号 重置完成 ✓" -ForegroundColor Green
}

# ========== 3. 逐个重置目标 GPU ==========
foreach ($单个序号 in $序号) {
    Reset-SingleGpu -Gpu序号 $单个序号
}

Write-Host "`n全部完成(共处理 $($序号.Count) 个 GPU)" -ForegroundColor Green
Write-Host "提示:若利用率/功耗/温度仍为异常组合,可能需要重启整机" -ForegroundColor DarkGray