""" BC-250 (gfx1010) Softmax Fix Test Tests if manual softmax works where fused kernel crashes with VGPR overflow. """ import torch import time import os import gc print(f'Device: {torch.cuda.get_device_name(0)}') print() def safe_softmax(x, dim=-1): """Manual softmax using elementwise ops - avoids fused kernel VGPR overflow.""" x_max = x.max(dim=dim, keepdim=True).values exp_x = torch.exp(x - x_max) return exp_x / exp_x.sum(dim=dim, keepdim=True) # Test 1: Does safe_softmax work where torch.softmax crashes? print('=== Test 1: safe_softmax on [1, 24, 512, 4096] ===', flush=True) try: x = torch.randn(1, 24, 512, 4096, device='cuda', dtype=torch.float32) torch.cuda.synchronize() t = time.time() y = safe_softmax(x, dim=-1) torch.cuda.synchronize() print(f' OK: {time.time()-t:.3f}s', flush=True) # Verify correctness assert y.min() >= 0, "Negative values in softmax" assert abs(y.sum(dim=-1).mean().item() - 1.0) < 0.001, "Softmax doesn't sum to 1" print(f' Correctness: OK (sum={y.sum(dim=-1).mean().item():.6f})', flush=True) del x, y gc.collect() torch.cuda.empty_cache() except Exception as e: print(f' FAIL: {e}', flush=True) # Test 2: Full attention pipeline with safe_softmax print('=== Test 2: Full attention with safe_softmax (24h, 4096 seq) ===', flush=True) try: heads, seq, hd = 24, 4096, 128 chunk = 512 q = torch.randn(1, heads, seq, hd, device='cuda', dtype=torch.float32) k = torch.randn(1, heads, seq, hd, device='cuda', dtype=torch.float32) v = torch.randn(1, heads, seq, hd, device='cuda', dtype=torch.float32) out = torch.zeros(1, heads, seq, hd, device='cuda', dtype=torch.float32) scale = hd ** -0.5 torch.cuda.synchronize() t = time.time() for i in range(0, seq, chunk): q_chunk = q[:, :, i:i+chunk, :] * scale scores = torch.matmul(q_chunk, k.transpose(-2, -1)) attn = safe_softmax(scores, dim=-1) out[:, :, i:i+chunk, :] = torch.matmul(attn, v) del scores, attn torch.cuda.synchronize() elapsed = time.time() - t print(f' OK: {elapsed:.3f}s', flush=True) del q, k, v, out gc.collect() torch.cuda.empty_cache() except Exception as e: print(f' FAIL: {e}', flush=True) # Test 3: Also verify torch.softmax on SMALL tensors still works print('=== Test 3: torch.softmax on small tensor [1, 8, 256, 256] ===', flush=True) try: x = torch.randn(1, 8, 256, 256, device='cuda', dtype=torch.float32) torch.cuda.synchronize() y = torch.softmax(x, dim=-1) torch.cuda.synchronize() print(f' OK (native softmax works on small tensors)', flush=True) del x, y except Exception as e: print(f' FAIL: {e}', flush=True) # Test 4: Find the threshold where torch.softmax breaks print('=== Test 4: Find native softmax failure threshold ===', flush=True) for last_dim in [256, 512, 1024, 2048, 4096]: try: x = torch.randn(1, 24, 512, last_dim, device='cuda', dtype=torch.float32) torch.cuda.synchronize() y = torch.softmax(x, dim=-1) torch.cuda.synchronize() print(f' dim={last_dim}: OK', flush=True) del x, y gc.collect() torch.cuda.empty_cache() except Exception as e: print(f' dim={last_dim}: FAIL ({e})', flush=True) gc.collect() torch.cuda.empty_cache() break # Test 5: Test SDPA (scaled_dot_product_attention) - what ComfyUI actually uses print('=== Test 5: F.scaled_dot_product_attention ===', flush=True) try: q = torch.randn(1, 24, 4096, 128, device='cuda', dtype=torch.float32) k = torch.randn(1, 24, 4096, 128, device='cuda', dtype=torch.float32) v = torch.randn(1, 24, 4096, 128, device='cuda', dtype=torch.float32) torch.cuda.synchronize() t = time.time() out = torch.nn.functional.scaled_dot_product_attention(q, k, v) torch.cuda.synchronize() print(f' SDPA OK: {time.time()-t:.3f}s', flush=True) del q, k, v, out except Exception as e: print(f' SDPA FAIL: {e}', flush=True) # Test 5b: SDPA with smaller dims print('=== Test 5b: SDPA smaller (8h, 1024 seq) ===', flush=True) try: q = torch.randn(1, 8, 1024, 128, device='cuda', dtype=torch.float32) k = torch.randn(1, 8, 1024, 128, device='cuda', dtype=torch.float32) v = torch.randn(1, 8, 1024, 128, device='cuda', dtype=torch.float32) torch.cuda.synchronize() t = time.time() out = torch.nn.functional.scaled_dot_product_attention(q, k, v) torch.cuda.synchronize() print(f' SDPA OK: {time.time()-t:.3f}s', flush=True) del q, k, v, out except Exception as e: print(f' SDPA FAIL: {e}', flush=True) print() print('=== ALL DONE ===', flush=True) torch.cuda.synchronize() os._exit(0)