DreamVAE / scripts /speed_bench.py
ryanontheinside's picture
DreamVAE initial release
53e74f7
Raw
History Blame Contribute Delete
5.4 kB
"""Isolated speed benchmark: teacher vs student decoder, 10s real audio."""
import torch
import time
import math
from torch.nn.utils import weight_norm
import torch.nn as nn
class Snake1d(nn.Module):
def __init__(self, hidden_dim, logscale=True):
super().__init__()
self.alpha = nn.Parameter(torch.zeros(1, hidden_dim, 1))
self.beta = nn.Parameter(torch.zeros(1, hidden_dim, 1))
self.logscale = logscale
def forward(self, x):
shape = x.shape
a = self.alpha if not self.logscale else torch.exp(self.alpha)
b = self.beta if not self.logscale else torch.exp(self.beta)
x = x.reshape(shape[0], shape[1], -1)
x = x + (b + 1e-9).reciprocal() * torch.sin(a * x).pow(2)
return x.reshape(shape)
class FastResidualUnit(nn.Module):
def __init__(self, dim, dilation=1):
super().__init__()
pad = ((7 - 1) * dilation) // 2
self.snake1 = Snake1d(dim)
self.conv1 = weight_norm(nn.Conv1d(dim, dim, 7, dilation=dilation, padding=pad))
self.snake2 = Snake1d(dim)
self.conv2 = weight_norm(nn.Conv1d(dim, dim, 1))
def forward(self, x):
h = self.conv1(self.snake1(x))
h = self.conv2(self.snake2(h))
pad = (x.shape[-1] - h.shape[-1]) // 2
if pad > 0:
x = x[..., pad:-pad]
return x + h
class FastDecoderBlock(nn.Module):
def __init__(self, in_dim, out_dim, stride=1):
super().__init__()
self.snake1 = Snake1d(in_dim)
self.conv_t = weight_norm(nn.ConvTranspose1d(in_dim, out_dim, 2 * stride, stride=stride, padding=math.ceil(stride / 2)))
self.res1 = FastResidualUnit(out_dim, 1)
self.res2 = FastResidualUnit(out_dim, 3)
def forward(self, x):
x = self.snake1(x)
x = self.conv_t(x)
x = self.res1(x)
x = self.res2(x)
return x
class FastOobleckDecoder(nn.Module):
def __init__(self, channels=128, input_channels=64, audio_channels=2, upsampling_ratios=None, channel_multiples=None):
super().__init__()
upsampling_ratios = upsampling_ratios or [10, 6, 4, 4, 2]
channel_multiples = channel_multiples or [1, 2, 4, 8, 8]
cm = [1] + channel_multiples
self.conv1 = weight_norm(nn.Conv1d(input_channels, channels * cm[-1], 7, padding=3))
blocks = []
for i, s in enumerate(upsampling_ratios):
blocks.append(FastDecoderBlock(channels * cm[len(upsampling_ratios) - i], channels * cm[len(upsampling_ratios) - i - 1], s))
self.blocks = nn.ModuleList(blocks)
self.final_snake = Snake1d(channels)
self.conv2 = weight_norm(nn.Conv1d(channels, audio_channels, 7, padding=3, bias=False))
def forward(self, z):
x = self.conv1(z)
for b in self.blocks:
x = b(x)
return self.conv2(self.final_snake(x))
def main():
device = "cuda"
torch.cuda.empty_cache()
# Load student
print("Loading student...")
ckpt = torch.load("checkpoints/fast_decoder_v3/student_step595000.pt", map_location=device, weights_only=False)
student = FastOobleckDecoder().to(device).eval()
student.load_state_dict(ckpt["student_state_dict"])
del ckpt
torch.cuda.empty_cache()
# Load teacher
print("Loading teacher...")
from diffusers import AutoencoderOobleck
vae = AutoencoderOobleck.from_pretrained("ACE-Step/Ace-Step1.5", subfolder="vae").to(device, dtype=torch.float32).eval()
teacher = vae.decoder
# Encode real audio to get latent
print("Encoding real audio...")
import soundfile as sf
data, sr = sf.read("tests/fixtures/techno.wav", dtype="float32")
wav = torch.tensor(data, dtype=torch.float32).T[:, :480000].unsqueeze(0).to(device) # 10s
with torch.no_grad():
z = vae.encode(wav).latent_dist.sample()
# Free everything except teacher decoder and student
del vae, wav
torch.cuda.empty_cache()
print(f"Latent shape: {list(z.shape)} (10s of audio)")
print(f"GPU memory: {torch.cuda.memory_allocated()/1e6:.0f}MB allocated\n")
# Warmup
print("Warming up...")
for _ in range(10):
with torch.no_grad():
teacher(z)
student(z)
torch.cuda.synchronize()
# Benchmark teacher
print("Benchmarking teacher (20 trials)...")
tt = []
for _ in range(20):
torch.cuda.synchronize()
t0 = time.perf_counter()
with torch.no_grad():
teacher(z)
torch.cuda.synchronize()
tt.append(time.perf_counter() - t0)
# Benchmark student
print("Benchmarking student (20 trials)...")
st = []
for _ in range(20):
torch.cuda.synchronize()
t0 = time.perf_counter()
with torch.no_grad():
student(z)
torch.cuda.synchronize()
st.append(time.perf_counter() - t0)
print(f"\n{'='*50}")
print(f"Teacher: {sum(tt)/len(tt)*1000:.1f}ms avg (min {min(tt)*1000:.1f}, max {max(tt)*1000:.1f})")
print(f"Student: {sum(st)/len(st)*1000:.1f}ms avg (min {min(st)*1000:.1f}, max {max(st)*1000:.1f})")
print(f"Speedup: {(sum(tt)/len(tt))/(sum(st)/len(st)):.2f}x")
print(f"{'='*50}")
if __name__ == "__main__":
main()