97 lines
3.1 KiB
Python
97 lines
3.1 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
ROCm MIOpen Cache Warmer for MinerU
|
||
|
||
在 AMD RDNA 架构上,MIOpen 遇到新尺寸的卷积运算时需要搜索最优 kernel(冷启动)。
|
||
预热脚本提前跑一遍常用尺寸,将 kernel 缓存到 ~/.cache/miopen/,避免运行时等待。
|
||
|
||
缓存持久化到磁盘,重启不丢失;只有升级 ROCm 后才需要重新跑。
|
||
|
||
用法:
|
||
python cache_warmer.py --device cuda --max_side 960 --step 32
|
||
"""
|
||
|
||
import argparse
|
||
import torch
|
||
import torch.nn as nn
|
||
import torch.nn.functional as F
|
||
from tqdm import tqdm
|
||
|
||
|
||
def get_args():
|
||
p = argparse.ArgumentParser(description="ROCm MIOpen Cache Warmer")
|
||
p.add_argument("--device", type=str, default="cuda")
|
||
p.add_argument("--max_side", type=int, default=960)
|
||
p.add_argument("--step", type=int, default=32)
|
||
return p.parse_args()
|
||
|
||
|
||
class MockOCRModel(nn.Module):
|
||
"""模拟 MinerU OCR 模型的卷积结构,覆盖 MIOpen 常用 kernel 尺寸。"""
|
||
|
||
def __init__(self, in_channels: int = 3):
|
||
super().__init__()
|
||
self.stem = nn.Conv2d(in_channels, 16, 3, stride=2, padding=1)
|
||
self.dw_3x3 = nn.Conv2d(16, 16, 3, stride=1, padding=1, groups=16)
|
||
self.pw_1 = nn.Conv2d(16, 64, 1)
|
||
self.dw_5x5 = nn.Conv2d(64, 64, 5, stride=2, padding=2, groups=64)
|
||
self.pw_2 = nn.Conv2d(64, 128, 1)
|
||
self.dw_3x3_s2 = nn.Conv2d(128, 128, 3, stride=2, padding=1, groups=128)
|
||
self.pw_3 = nn.Conv2d(128, 256, 1)
|
||
self.out_conv = nn.Conv2d(256, 64, 1)
|
||
self.binarize_conv = nn.Conv2d(64, 1, 3, stride=1, padding=1)
|
||
self.act = nn.ReLU()
|
||
|
||
def forward(self, x):
|
||
x = self.stem(x)
|
||
x = self.act(x)
|
||
x = self.dw_3x3(x)
|
||
x = self.pw_1(x)
|
||
x = self.dw_5x5(x)
|
||
x = self.act(x)
|
||
x = self.pw_2(x)
|
||
x = self.dw_3x3_s2(x)
|
||
x = self.pw_3(x)
|
||
x = self.out_conv(x)
|
||
x = F.interpolate(x, scale_factor=2, mode="bilinear", align_corners=True)
|
||
x = self.binarize_conv(x)
|
||
return x
|
||
|
||
|
||
def main():
|
||
args = get_args()
|
||
assert torch.cuda.is_available(), "GPU not available"
|
||
device = torch.device(args.device)
|
||
|
||
print("=" * 50)
|
||
print("ROCm MIOpen Cache Warmer")
|
||
print(f" GPU : {torch.cuda.get_device_name(0)}")
|
||
print(f" ROCm : {torch.version.hip}")
|
||
print(f" Cache : ~/.cache/miopen/")
|
||
print("=" * 50)
|
||
|
||
model = MockOCRModel().to(device).eval()
|
||
sizes = list(range(64, args.max_side + 1, args.step))
|
||
combos = [(h, w) for h in sizes for w in sizes]
|
||
|
||
print(f"Warming {len(combos)} shapes ({len(sizes)}x{len(sizes)} grid, "
|
||
f"step={args.step})...")
|
||
|
||
ok = 0
|
||
with torch.no_grad():
|
||
for h, w in tqdm(combos, desc="Warming"):
|
||
try:
|
||
model(torch.zeros((1, 3, h, w), device=device, dtype=torch.float32))
|
||
ok += 1
|
||
except RuntimeError as e:
|
||
if "out of memory" in str(e):
|
||
torch.cuda.empty_cache()
|
||
# 其他错误跳过,不影响后续
|
||
|
||
print(f"\nDone! {ok}/{len(combos)} shapes cached (~3–4 min)")
|
||
print("Kernels saved to ~/.cache/miopen/")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|