Dell G15 5520 RTX 3050 Laptop 显卡问题导致游戏/模型训练崩溃蓝屏重启的一次排查记录

结论

显卡的显存在较高频率下不稳定,需要手动降频。最终我在核心:405-2100 MHz 显存:405-5501 MHz下可以保持较高的稳定性,不会出现初始参数下的蓝屏重启的问题。

硬件环境

电脑:Dell G15 5520

CPU:Intel Core i5-12500H

核显:Intel Iris Xe

独显:NVIDIA GeForce RTX 3050 Laptop GPU,4GB VRAM

背景

从我拿到这个电脑的第一天起,只要我一玩游戏,电脑就蓝屏,或者游戏出现卡死的状况,比如原神,赛博朋克,逃离鸭科夫,米塔,地平线4,等等,3A大作,独立游戏全都一视同仁,没有一个能正常游玩半个小时以上。但是,其中有两个游戏非常特殊,空洞骑士和奥日,这两个游戏没有崩溃过,这也导致我当时认为不是电脑的问题,可能是显卡太差了,性能不够带不起来游戏的3D渲染,虽然逃离鸭科夫和米塔也带不起来有点扯淡,但我当时就这样说服了自己。

平时打游戏比较少,加上一开始的时候我拿它训练模型什么的也没事,就没有管他。直到前几天,我在训练一个小模型的时候,发生了和游戏崩溃一样的事,电脑崩溃重启了,这直接撞到我的熟练区了,一个几十MB的小模型你个3050训练不了这不胡诌吗,这显卡绝对有问题啊。然后我又去找了一下游戏的一些性能需求,打算新仇旧帐一块算,结果一看地平线4都18年的游戏了,他的推荐显卡的性能还没我这个3050强呢。其实当时找到这这个问题之后我也没想修他,想着这个电脑都用了4年了,直接618换个新的得了。嘿,一看今年的笔记本,我去年买一台今年买二手都能赚一点。得了吧,修修补补又三年,先修吧,也就有了这个排查记录。

问题复现

地平线 4

地平线 4运行大概20分钟后画面卡死,声音仍然继续播放,但窗口无法正常操作,显卡负载归零,弹窗强制关闭。在同一时间,Windows 事件查看器里出现了 NVIDIA 驱动相关的记录。

image-20260623143421786

image-20260623143939454

GWN 模型训练

GWN模型训练,环境为PyTorch 2.5.1+cu121,CUDA 12.1,batch=16,epochs=20,偶尔会出现电脑黑屏后重启,大多数情况下训练过程报错终止。训练日志记录如下。

正常训练

遇到非法内存访问

[case] device cuda epochs 20 batch 16
[train] device = cuda
[epoch   1] loss=2.5040 val_rmse=7.0906 lr=6.0e-05 steps=120/120
[epoch   2] loss=1.7521 val_rmse=4.5731 lr=1.2e-04 steps=120/120
[epoch   3] loss=1.0894 val_rmse=3.2107 lr=1.8e-04 steps=120/120

[train] WARN training crashed:
CUDA error: an illegal memory access was encountered

CUDA kernel errors might be asynchronously reported at some other API call,
so the stacktrace below might be incorrect.

CUDA 同步定位

CUDA 同步定位,同样出现出现任务执行失败。

[case] device cuda epochs 20 batch 16
[train] device = cuda
[epoch   1] loss=2.5048 val_rmse=7.1013 lr=6.0e-05 steps=120/120
[epoch   2] loss=1.7579 val_rmse=4.6111 lr=1.2e-04 steps=120/120

[train] WARN training crashed:
cuDNN error: CUDNN_STATUS_EXECUTION_FAILED

关闭 cuDNN

验证指标变成了 nan,出现了数值异常,但是此处在我降频之后就被修好了。emmm,但是我印象里这个错误还有其他的触发方式,比如我之前自己搓了一个模型训练的时候也有这个nan的问题,我那时是在模型里加了一个norm层解决的,不理解。

[case] device cuda epochs 20 batch 16
[train] device = cuda
[epoch   1] loss=2.5040 val_rmse=7.0905 lr=6.0e-05 steps=120/120
[epoch   2] loss=1.7642 val_rmse=nan lr=1.2e-04 steps=119/120
[epoch   3] loss=1.0848 val_rmse=nan lr=1.8e-04 steps=120/120
[epoch   4] loss=0.9376 val_rmse=nan lr=2.4e-04 steps=119/120
...
[epoch  16] loss=0.8188 val_rmse=nan lr=5.2e-05 steps=117/120

[train] early stop @ epoch 16

小 batch 训练

小 batch 训练,显存占用仅556 MiB,温度约 70°C,仍然触发 CUDA 错误,不是显存爆了。

[case] device cuda epochs 8 batch 4
[train] device = cuda
[epoch   1] loss=2.1730 val_rmse=4.9791 lr=6.0e-05 steps=477/477
[epoch   2] loss=1.1130 val_rmse=3.0742 lr=1.2e-04 steps=477/477
[epoch   3] loss=0.9278 val_rmse=2.7030 lr=1.8e-04 steps=477/477
[epoch   4] loss=0.8829 val_rmse=2.5214 lr=2.4e-04 steps=477/477

[train] WARN training crashed:
CUDA error: an illegal memory access was encountered

排查

先排查驱动的问题,将驱动更新为官方推荐版本。直接下载dell官方推荐的驱动Dell Drivers & Downloads,然后为了彻底卸载之前的驱动我又下载了Wagnardsoft / Display Driver Uninstaller 官方下载,之后按照下图的执行顺序清除现有驱动并进行重装。

image-20260623171350212

再跑训练,还是不行。那没办法了,去查一下硬件吧。我用Dell自己的SupportAssist跑了一遍官方硬件扫描。大概流程是打开 SupportAssist,进支持 / Support,选 I want to check a specific piece of hardware,找 Video Card / 显卡,选 Stress Test / 压力测试。但是,我跑完这一遍检测没有任何问题。

image-20260623170713401

image-20260623161759852

于是我换了一个更专业的工具OCCT 官方下载,跑了一遍30分钟高负载的3D Adaptive和VRAM 检测,还是没有任何问题。

image-20260623170222310

这就很神奇了,再跑模型训练还是出现问题,明明测试的负载要高于训练,但是什么问题也检测不出来,系统也没有报错,但是一跑游戏马上就卡了。

驱动没有问题,使用的是dell官网上推荐的版本,并且已经使用DDU完全清除了之前的驱动。OCCT的测试没有问题,说明整体上看这块显卡硬件上没什么大问题,核心和显存都没有那种直接的坏。那么问题应该是显卡中的一些组件在某些情况下不稳定。显卡不稳定,但是什么会导致不稳定呢,我想到了内存,对内存进行超频的话,内存出错的概率会增加,那么显卡是不是也是这个问题呢,要不降频试试。于是我直接将核心和显存的最大频率砍到1200MHz和5501MHz。哎,好了,模型训练也正常了,游戏也能跑了。但是这样功率限制太多了,我就让gpt去跑脚本调参,看看哪个配比没有阉割太多,又能保证稳定。

参数 结果 最高功耗
1200 / 6001 12 epoch 通过 27.62W
1500 / 5501 12 epoch 通过 35.35W
1650 / 5501 12 epoch 通过 42.56W
1800 / 5501 12 epoch 通过 51.83W
2100 / 5501 12 epoch 通过 70.56W
2100 / 5501 30 epoch 确认通过 70.31W

这个显卡的初始频率是2100 / 6001,也就是说我将显存或者核心的频率降低一点,问题就解决了。

脚本

直接命令

nvidia-smi -lgc 405,2100
nvidia-smi -lmc 405,5501

nvidia-smi --query-gpu=name,pstate,temperature.gpu,power.draw,clocks.gr,clocks.mem --format=csv,noheader,nounits

最终调频脚本:set-nvidia-tuned-clocks.ps1

param(
  [int]$MaxGraphicsMHz = 2100,
  [int]$MaxMemoryMHz = 5501
)

$ErrorActionPreference = "Continue"

Write-Host "Setting NVIDIA tuned clocks: graphics 405-$MaxGraphicsMHz MHz, memory 405-$MaxMemoryMHz MHz"
nvidia-smi -lgc 405,$MaxGraphicsMHz
nvidia-smi -lmc 405,$MaxMemoryMHz

Write-Host ""
Write-Host "Current status:"
nvidia-smi --query-gpu=name,pstate,temperature.gpu,power.draw,clocks.gr,clocks.mem --format=csv,noheader,nounits

运行方式:

powershell -ExecutionPolicy Bypass -File "C:\job\Vue\personalized-navigation-system\tools\gpu-repro\set-nvidia-tuned-clocks.ps1"

恢复默认频率脚本:reset-nvidia-clocks.ps1

$ErrorActionPreference = "Continue"

Write-Host "Resetting NVIDIA locked graphics and memory clocks."
nvidia-smi -rgc
nvidia-smi -rmc

Write-Host ""
Write-Host "Current status:"
nvidia-smi --query-gpu=name,pstate,temperature.gpu,power.draw,clocks.gr,clocks.mem --format=csv,noheader,nounits

开机自动应用脚本:startup-apply-nvidia-tuned-clocks.ps1

$ErrorActionPreference = "Continue"

$scriptRoot = Split-Path -Parent $MyInvocation.MyCommand.Path
$logDir = Join-Path $scriptRoot "startup-logs"
New-Item -ItemType Directory -Force -Path $logDir | Out-Null
$logPath = Join-Path $logDir ("startup-apply-" + (Get-Date -Format "yyyyMMdd-HHmmss") + ".log")

function Write-Log([string]$message) {
  $line = "$(Get-Date -Format o) $message"
  $line | Tee-Object -FilePath $logPath -Append
}

Write-Log "Startup NVIDIA tuned clock apply started."
Write-Log "ScriptRoot=$scriptRoot"

Start-Sleep -Seconds 25

$target = Join-Path $scriptRoot "set-nvidia-tuned-clocks.ps1"
if (-not (Test-Path -LiteralPath $target)) {
  Write-Log "ERROR: target script missing: $target"
  exit 1
}

$success = $false
for ($i = 1; $i -le 8; $i++) {
  Write-Log "Attempt ${i}: applying tuned clocks."
  $output = & powershell.exe -NoProfile -ExecutionPolicy Bypass -File $target 2>&1
  foreach ($line in $output) {
    Write-Log ([string]$line)
  }

  $check = & nvidia-smi --query-gpu=clocks.gr,clocks.mem --format=csv,noheader,nounits 2>&1
  Write-Log "Clock check: $check"

  if (($output -join "`n") -match "GPU clocks set" -and ($output -join "`n") -match "Memory clocks set") {
    $success = $true
    break
  }

  Start-Sleep -Seconds 10
}

if ($success) {
  Write-Log "Startup NVIDIA tuned clock apply finished successfully."
  exit 0
}

Write-Log "ERROR: Startup NVIDIA tuned clock apply did not confirm success."
exit 2

使用教程

1. 放置脚本

先新建一个文件夹,用来专门存放调频脚本,放哪都行,没关系,例如:

C:\gpu-clock\

然后把下面两个脚本放到同一个文件夹里:

C:\gpu-clock\set-nvidia-tuned-clocks.ps1
C:\gpu-clock\startup-apply-nvidia-tuned-clocks.ps1

如果还需要恢复默认频率,也可以把恢复脚本一起放进去:

C:\gpu-clock\reset-nvidia-clocks.ps1

一定要注意:
set-nvidia-tuned-clocks.ps1startup-apply-nvidia-tuned-clocks.ps1 必须放在同一个文件夹里。

原因是开机自动应用脚本里有这两行:

$scriptRoot = Split-Path -Parent $MyInvocation.MyCommand.Path
$target = Join-Path $scriptRoot "set-nvidia-tuned-clocks.ps1"

它会先找到 startup-apply-nvidia-tuned-clocks.ps1 自己所在的目录,然后在同一个目录里寻找:

set-nvidia-tuned-clocks.ps1

如果两个脚本不在同一个文件夹里,开机自动脚本会找不到真正的调频脚本,日志里会出现:

ERROR: target script missing

2. 确认 nvidia-smi 可以使用

打开 PowerShell,执行:

nvidia-smi

如果能看到显卡信息,说明 nvidia-smi 可以正常使用。

如果提示找不到命令,可以改用完整路径:

"C:\Program Files\NVIDIA Corporation\NVSMI\nvidia-smi.exe"

如果你的电脑必须使用完整路径,那么脚本里的 nvidia-smi 也需要改成完整路径,例如:

& "C:\Program Files\NVIDIA Corporation\NVSMI\nvidia-smi.exe" -lgc 405,2100
& "C:\Program Files\NVIDIA Corporation\NVSMI\nvidia-smi.exe" -lmc 405,5501

3. 手动测试调频是否生效

先用管理员权限打开 PowerShell,然后执行:

powershell -ExecutionPolicy Bypass -File "C:\gpu-clock\set-nvidia-tuned-clocks.ps1"

成功后一般会看到类似输出:

GPU clocks set
Memory clocks set

然后查看当前频率:

nvidia-smi --query-gpu=name,pstate,temperature.gpu,power.draw,clocks.gr,clocks.mem --format=csv,noheader,nounits

注意:空闲状态下显卡频率可能会降到很低,这是正常的。锁定的是允许的频率范围,不代表显卡空闲时一定固定在 2100 / 5501 MHz。

4. 设置开机自动应用

因为锁频命令通常需要管理员权限,所以我使用 Windows 任务计划程序来实现登录后自动运行。

管理员权限打开 PowerShell,执行:

$taskName = "NVIDIA Tuned Clocks 2100-5501"
$scriptPath = "C:\gpu-clock\startup-apply-nvidia-tuned-clocks.ps1"

$action = New-ScheduledTaskAction `
  -Execute "powershell.exe" `
  -Argument "-NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File `"$scriptPath`""

$trigger = New-ScheduledTaskTrigger -AtLogOn

$principal = New-ScheduledTaskPrincipal `
  -UserId $env:USERNAME `
  -LogonType Interactive `
  -RunLevel Highest

Register-ScheduledTask `
  -TaskName $taskName `
  -Action $action `
  -Trigger $trigger `
  -Principal $principal `
  -Description "Apply NVIDIA tuned graphics/memory clocks at user logon."

这里的关键点是:

-AtLogOn:用户登录时运行
-RunLevel Highest:使用最高权限运行
-WindowStyle Hidden:后台静默执行,不弹 PowerShell 窗口

5. 手动运行一次计划任务

创建完成后,可以不用重启,直接手动触发一次:

Start-ScheduledTask -TaskName "NVIDIA Tuned Clocks 2100-5501"

等待半分钟左右,然后查看日志:

C:\gpu-clock\startup-logs\

如果日志里出现:

GPU clocks set
Memory clocks set
Startup NVIDIA tuned clock apply finished successfully.

说明开机自动调频脚本可以正常工作。

6. 检查计划任务是否存在

Get-ScheduledTask -TaskName "NVIDIA Tuned Clocks 2100-5501"

也可以打开:

任务计划程序 -> 任务计划程序库

找到:

NVIDIA Tuned Clocks 2100-5501

7. 删除开机自动应用

如果以后不想登录后自动锁频,可以删除这个计划任务:

Unregister-ScheduledTask -TaskName "NVIDIA Tuned Clocks 2100-5501" -Confirm:$false

8. 恢复默认频率

删除计划任务只是不再开机自动设置,不会立刻恢复当前频率。

如果要立刻恢复 NVIDIA 默认频率,执行:

nvidia-smi -rgc
nvidia-smi -rmc

或者运行恢复脚本:

powershell -ExecutionPolicy Bypass -File "C:\gpu-clock\reset-nvidia-clocks.ps1"

恢复后可以再查一次:

nvidia-smi --query-gpu=name,pstate,temperature.gpu,power.draw,clocks.gr,clocks.mem --format=csv,noheader,nounits