重置GPU.ps1

<#PSScriptInfo
 
.VERSION 1.0.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
 
.PROJECTURI
 
.ICONURI
 
.EXTERNALMODULEDEPENDENCIES
 
.REQUIREDSCRIPTS
 
.EXTERNALSCRIPTDEPENDENCIES
 
.RELEASENOTES
 
 
#>


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


[CmdletBinding()]
param(
    [Parameter(Mandatory = $true, Position = 0, HelpMessage = "GPU 序号(nvidia-smi 中的 index)")]
    [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 的 PCI 总线号 ==========
Write-Host "==> 查询 GPU $序号 的 PCI 总线号..." -ForegroundColor Cyan
$所有行 = nvidia-smi --query-gpu=index,pci.bus_id --format=csv,noheader
$目标行 = $所有行 | Where-Object { [int](($_ -split ',')[0].Trim()) -eq $序号 }

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

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

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

if ($已连接显示器) {
    Write-Error "GPU $序号 连接了显示器,禁用会导致黑屏,操作已中止"
    exit 1
}
Write-Host " Display Attached: No (可安全重置)"

# ========== 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 设备"
    exit 1
}
Write-Host " InstanceId: $实例ID"

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

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

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

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

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

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

Write-Host "`nGPU $序号 重置完成 ✓" -ForegroundColor Green
Write-Host "提示:若利用率/功耗/温度仍为异常组合,可能需要重启整机" -ForegroundColor DarkGray