157 lines
6.9 KiB
Python
157 lines
6.9 KiB
Python
"""Fix SDPA patch for BC-250 gfx1010: always use manual SDPA on CUDA"""
|
|
import sys
|
|
|
|
path = '/home/fabian/ComfyUI/bc250_softmax_patch.py'
|
|
with open(path, 'r') as f:
|
|
content = f.read()
|
|
|
|
# Fix 1: patched_sdpa - ALWAYS use manual SDPA on CUDA (no threshold)
|
|
old_sdpa = '''def patched_sdpa(query, key, value, attn_mask=None, dropout_p=0.0, is_causal=False, scale=None):
|
|
S = key.size(-2)
|
|
if query.is_cuda and S > SAFE_SOFTMAX_THRESHOLD:
|
|
if not getattr(patched_sdpa, '_logged', False):
|
|
logger.warning(f"[BC-250] Manual SDPA: Q={list(query.shape)}, S={S}")
|
|
patched_sdpa._logged = True
|
|
return _safe_sdpa(query, key, value, attn_mask=attn_mask,
|
|
dropout_p=dropout_p, is_causal=is_causal, scale=scale)
|
|
return _original_sdpa(query, key, value, attn_mask=attn_mask,
|
|
dropout_p=dropout_p, is_causal=is_causal, scale=scale)'''
|
|
|
|
new_sdpa = '''def patched_sdpa(query, key, value, attn_mask=None, dropout_p=0.0, is_causal=False, scale=None):
|
|
# gfx1010: ALWAYS use manual SDPA on CUDA — built-in math backend kernel hangs
|
|
if query.is_cuda:
|
|
if not getattr(patched_sdpa, '_logged', False):
|
|
S = key.size(-2)
|
|
logger.warning(f"[BC-250] Manual SDPA (ALWAYS): Q={list(query.shape)}, S={S}")
|
|
patched_sdpa._logged = True
|
|
return _safe_sdpa(query, key, value, attn_mask=attn_mask,
|
|
dropout_p=dropout_p, is_causal=is_causal, scale=scale)
|
|
return _original_sdpa(query, key, value, attn_mask=attn_mask,
|
|
dropout_p=dropout_p, is_causal=is_causal, scale=scale)'''
|
|
|
|
if old_sdpa not in content:
|
|
print("ERROR: patched_sdpa not found!")
|
|
sys.exit(1)
|
|
content = content.replace(old_sdpa, new_sdpa)
|
|
print("OK: patched_sdpa → always manual on CUDA")
|
|
|
|
# Fix 2: _safe_sdpa - add sync after GPU matmul+softmax operations
|
|
old_safe = '''def _safe_sdpa(query, key, value, attn_mask=None, dropout_p=0.0, is_causal=False, scale=None):
|
|
L, S = query.size(-2), key.size(-2)
|
|
if scale is None:
|
|
scale = query.size(-1) ** -0.5
|
|
attn_weight = torch.matmul(query, key.transpose(-2, -1)) * scale
|
|
if is_causal:
|
|
causal_mask = torch.triu(torch.ones(L, S, dtype=torch.bool, device=query.device), diagonal=1)
|
|
attn_weight = attn_weight.masked_fill(causal_mask, float('-inf'))
|
|
if attn_mask is not None:
|
|
if attn_mask.dtype == torch.bool:
|
|
attn_weight = attn_weight.masked_fill(~attn_mask, float('-inf'))
|
|
else:
|
|
attn_weight = attn_weight + attn_mask
|
|
attn_weight = _safe_softmax_impl(attn_weight, dim=-1)
|
|
if dropout_p > 0.0:
|
|
attn_weight = torch.nn.functional.dropout(attn_weight, p=dropout_p)
|
|
return torch.matmul(attn_weight, value)'''
|
|
|
|
new_safe = '''def _safe_sdpa(query, key, value, attn_mask=None, dropout_p=0.0, is_causal=False, scale=None):
|
|
L, S = query.size(-2), key.size(-2)
|
|
if scale is None:
|
|
scale = query.size(-1) ** -0.5
|
|
# gfx1010: sync between GPU ops to prevent kernel queue buildup + hang
|
|
attn_weight = torch.matmul(query, key.transpose(-2, -1)) * scale
|
|
if query.is_cuda:
|
|
torch.cuda.synchronize()
|
|
if is_causal:
|
|
causal_mask = torch.triu(torch.ones(L, S, dtype=torch.bool, device=query.device), diagonal=1)
|
|
attn_weight = attn_weight.masked_fill(causal_mask, float('-inf'))
|
|
if attn_mask is not None:
|
|
if attn_mask.dtype == torch.bool:
|
|
attn_weight = attn_weight.masked_fill(~attn_mask, float('-inf'))
|
|
else:
|
|
attn_weight = attn_weight + attn_mask
|
|
attn_weight = _safe_softmax_impl(attn_weight, dim=-1)
|
|
if query.is_cuda:
|
|
torch.cuda.synchronize()
|
|
if dropout_p > 0.0:
|
|
attn_weight = torch.nn.functional.dropout(attn_weight, p=dropout_p)
|
|
output = torch.matmul(attn_weight, value)
|
|
if query.is_cuda:
|
|
torch.cuda.synchronize()
|
|
return output'''
|
|
|
|
if old_safe not in content:
|
|
print("ERROR: _safe_sdpa not found!")
|
|
sys.exit(1)
|
|
content = content.replace(old_safe, new_safe)
|
|
print("OK: _safe_sdpa → sync after GPU ops")
|
|
|
|
# Fix 3: Also patch softmax to ALWAYS use manual on CUDA (same reason)
|
|
old_softmax = '''def patched_softmax(input, dim=None, _stacklevel=3, dtype=None):
|
|
if dim is None:
|
|
dim = -1
|
|
if dtype is not None:
|
|
input = input.to(dtype)
|
|
if input.is_cuda and input.shape[dim] > SAFE_SOFTMAX_THRESHOLD:
|
|
if not getattr(patched_softmax, '_logged', False):
|
|
logger.warning(f"[BC-250] Manual F.softmax: shape={list(input.shape)}, dim={dim}")
|
|
patched_softmax._logged = True
|
|
return _safe_softmax_impl(input, dim)
|
|
return _original_softmax(input, dim=dim)'''
|
|
|
|
new_softmax = '''def patched_softmax(input, dim=None, _stacklevel=3, dtype=None):
|
|
if dim is None:
|
|
dim = -1
|
|
if dtype is not None:
|
|
input = input.to(dtype)
|
|
# gfx1010: ALWAYS use manual softmax on CUDA — native kernel unreliable
|
|
if input.is_cuda:
|
|
if not getattr(patched_softmax, '_logged', False):
|
|
logger.warning(f"[BC-250] Manual F.softmax (ALWAYS): shape={list(input.shape)}, dim={dim}")
|
|
patched_softmax._logged = True
|
|
return _safe_softmax_impl(input, dim)
|
|
return _original_softmax(input, dim=dim)'''
|
|
|
|
if old_softmax not in content:
|
|
print("WARNING: patched_softmax not found (may already be fixed)")
|
|
else:
|
|
content = content.replace(old_softmax, new_softmax)
|
|
print("OK: patched_softmax → always manual on CUDA")
|
|
|
|
# Fix 4: Same for tensor.softmax
|
|
old_tsm = '''def patched_tensor_softmax(self, dim=-1, dtype=None):
|
|
if dtype is not None:
|
|
self = self.to(dtype)
|
|
if self.is_cuda and self.shape[dim] > SAFE_SOFTMAX_THRESHOLD:
|
|
if not getattr(patched_tensor_softmax, '_logged', False):
|
|
logger.warning(f"[BC-250] Manual softmax: shape={list(self.shape)}, dim={dim}")
|
|
patched_tensor_softmax._logged = True
|
|
return _safe_softmax_impl(self, dim)
|
|
return _original_tensor_softmax(self, dim=dim)'''
|
|
|
|
new_tsm = '''def patched_tensor_softmax(self, dim=-1, dtype=None):
|
|
if dtype is not None:
|
|
self = self.to(dtype)
|
|
# gfx1010: ALWAYS use manual softmax on CUDA
|
|
if self.is_cuda:
|
|
if not getattr(patched_tensor_softmax, '_logged', False):
|
|
logger.warning(f"[BC-250] Manual softmax (ALWAYS): shape={list(self.shape)}, dim={dim}")
|
|
patched_tensor_softmax._logged = True
|
|
return _safe_softmax_impl(self, dim)
|
|
return _original_tensor_softmax(self, dim=dim)'''
|
|
|
|
if old_tsm not in content:
|
|
print("WARNING: patched_tensor_softmax not found (may already be fixed)")
|
|
else:
|
|
content = content.replace(old_tsm, new_tsm)
|
|
print("OK: patched_tensor_softmax → always manual on CUDA")
|
|
|
|
# Fix 5: Update version string
|
|
content = content.replace('v17 ready', 'v19 ready — ALL CUDA ops manual (no native kernels)')
|
|
content = content.replace('Comprehensive Monkey-Patch v17', 'Comprehensive Monkey-Patch v19')
|
|
|
|
with open(path, 'w') as f:
|
|
f.write(content)
|
|
|
|
print("\nALL PATCHES APPLIED — v19")
|