Device VAE on the 1xN mesh path (reclaims ~5s; 2-card 1024 E2E 1.68x->1.75x)
Browse files- ttnn_moss/pipeline_mesh.py +5 -1
- ttnn_moss/vae.py +15 -1
ttnn_moss/pipeline_mesh.py
CHANGED
|
@@ -20,7 +20,11 @@ from .dit_tp import TtMossImageTransformerTP
|
|
| 20 |
class MeshDiTPipeline(MossImagePipeline):
|
| 21 |
def __init__(self, mesh_device, weights_root, **kw):
|
| 22 |
self.mesh = mesh_device
|
| 23 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 24 |
super().__init__(mesh_device, weights_root=weights_root, **kw)
|
| 25 |
|
| 26 |
def _build_dit(self):
|
|
|
|
| 20 |
class MeshDiTPipeline(MossImagePipeline):
|
| 21 |
def __init__(self, mesh_device, weights_root, **kw):
|
| 22 |
self.mesh = mesh_device
|
| 23 |
+
# VAE decode runs ON-DEVICE by default, REPLICATED across the mesh dies
|
| 24 |
+
# (the decoder isn't the compute bulk and needs no sharding; from_torch
|
| 25 |
+
# replicates the input, vae.py's _mesh_to_torch takes die-0's result).
|
| 26 |
+
# This reclaims the host-VAE penalty on the mesh path. MOSS_VAE_DEVICE=0
|
| 27 |
+
# still forces the host diffusers decode (the validated fallback).
|
| 28 |
super().__init__(mesh_device, weights_root=weights_root, **kw)
|
| 29 |
|
| 30 |
def _build_dit(self):
|
ttnn_moss/vae.py
CHANGED
|
@@ -49,6 +49,20 @@ GN_EPS = 1e-6
|
|
| 49 |
_CORE = None # set per-device in decoder init
|
| 50 |
|
| 51 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 52 |
def _num_out_blocks(hw: int) -> int:
|
| 53 |
"""L1 tiling for DRAM-interleaved group_norm; larger HW -> more blocks."""
|
| 54 |
if hw <= 4096:
|
|
@@ -350,6 +364,6 @@ class TtFlux2VaeDecoder:
|
|
| 350 |
x = ttnn.to_memory_config(x, ttnn.DRAM_MEMORY_CONFIG)
|
| 351 |
x, H, W = self.conv_out(x, H, W)
|
| 352 |
|
| 353 |
-
out =
|
| 354 |
ttnn.deallocate(x)
|
| 355 |
return out.float().clamp(-1, 1)
|
|
|
|
| 49 |
_CORE = None # set per-device in decoder init
|
| 50 |
|
| 51 |
|
| 52 |
+
def _mesh_to_torch(device, t):
|
| 53 |
+
"""to_torch that is mesh-aware. On a 1xN mesh the decoder runs REPLICATED
|
| 54 |
+
(from_torch replicates), so its output tensor is distributed and a plain
|
| 55 |
+
ttnn.to_torch fails ('supply a mesh composer'). Compose the identical shards
|
| 56 |
+
and keep the first copy; on a single device, unchanged."""
|
| 57 |
+
try:
|
| 58 |
+
multi = device.get_num_devices() > 1
|
| 59 |
+
except Exception:
|
| 60 |
+
multi = False
|
| 61 |
+
if multi:
|
| 62 |
+
return ttnn.to_torch(t, mesh_composer=ttnn.ConcatMeshToTensor(device, dim=0))[0:1]
|
| 63 |
+
return ttnn.to_torch(t)
|
| 64 |
+
|
| 65 |
+
|
| 66 |
def _num_out_blocks(hw: int) -> int:
|
| 67 |
"""L1 tiling for DRAM-interleaved group_norm; larger HW -> more blocks."""
|
| 68 |
if hw <= 4096:
|
|
|
|
| 364 |
x = ttnn.to_memory_config(x, ttnn.DRAM_MEMORY_CONFIG)
|
| 365 |
x, H, W = self.conv_out(x, H, W)
|
| 366 |
|
| 367 |
+
out = _mesh_to_torch(self.device, x).reshape(1, H, W, self.conv_out.cout).permute(0, 3, 1, 2)
|
| 368 |
ttnn.deallocate(x)
|
| 369 |
return out.float().clamp(-1, 1)
|