Skip to content

Commit 49d016b

Browse files
committed
Refactor Phase B to use fused jax.lax.fori_loop for optimal multi-host execution
1 parent 13d1475 commit 49d016b

1 file changed

Lines changed: 35 additions & 29 deletions

File tree

src/maxdiffusion/pipelines/flux/flux2klein_pipeline.py

Lines changed: 35 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -256,43 +256,49 @@ def put_data_on_devices(x, sharding):
256256
prompt_embeds_jax = put_data_on_devices(prompt_embeds_jax, data_sharding)
257257

258258
# ---------------------------------------------------------------------
259-
# PHASE B: Denoising Loop (Flux Transformer)
259+
# PHASE B: Denoising Loop (Flux Transformer - Fused JAX fori_loop)
260260
# ---------------------------------------------------------------------
261261
max_logging.log(
262262
f"[PHASE B] Running {num_inference_steps}-step E2E Denoising Loop on a batch of {batch_size} images..."
263263
)
264264
t0 = time.perf_counter()
265265

266-
guidance_vec_val = None
267-
vec_val = None
268-
269-
for step_idx in range(num_inference_steps):
270-
timestep = scheduler_state.timesteps[step_idx]
271-
t_vec = jnp.array([timestep / 1000.0] * batch_size)
272-
273-
# Execute transformer forward pass step
274-
model_output = self._jitted_transformer_step(
275-
params, latents_jax, img_ids_val, prompt_embeds_jax, txt_ids_val, vec_val, t_vec, guidance_vec_val
276-
)
277-
278-
# Update latents using FlowMatch step
279-
latents_jax = self.scheduler.step(
280-
state=scheduler_state,
281-
model_output=model_output.sample,
282-
timestep=scheduler_state.timesteps[step_idx],
283-
sample=latents_jax,
284-
).prev_sample
285-
286-
# Print progress
287-
sigma_val = scheduler_state.sigmas[step_idx]
288-
max_logging.log(
289-
f" -> Step {step_idx}: Timestep = {scheduler_state.timesteps[step_idx]:.4f}, Sigma = {sigma_val:.4f}"
290-
)
291-
266+
timesteps_array = scheduler_state.timesteps
267+
sigmas_array = scheduler_state.sigmas
268+
269+
@jax.jit
270+
def denoise_loop(t_params, latents, prompt_embeds, txt_ids, img_ids, timesteps, sigmas):
271+
def step_fn(step_idx, current_latents):
272+
t_val = timesteps[step_idx] / 1000.0
273+
t_vec = jnp.full((batch_size,), t_val, dtype=current_latents.dtype)
274+
out = self.transformer.apply(
275+
{"params": t_params},
276+
hidden_states=current_latents,
277+
img_ids=img_ids,
278+
encoder_hidden_states=prompt_embeds,
279+
txt_ids=txt_ids,
280+
pooled_projections=None,
281+
timestep=t_vec,
282+
guidance=None,
283+
).sample
284+
dt = sigmas[step_idx + 1] - sigmas[step_idx]
285+
return current_latents + dt * out
286+
287+
return jax.lax.fori_loop(0, num_inference_steps, step_fn, latents)
288+
289+
latents_jax = denoise_loop(
290+
params,
291+
latents_jax,
292+
prompt_embeds_jax,
293+
txt_ids_val,
294+
img_ids_val,
295+
timesteps_array,
296+
sigmas_array,
297+
)
292298
latents_jax.block_until_ready()
293299

294-
trace["denoise_loop"] = time.perf_counter() - t0
295-
max_logging.log(f" -> [TIMING] Denoising Loop (Flux): {trace['denoise_loop']:.4f} seconds ⏱️")
300+
trace["denoise_loop"] = time.perf_counter() - t0
301+
max_logging.log(f" -> [TIMING] Denoising Loop (Flux): {trace['denoise_loop']:.4f} seconds ⏱️")
296302

297303
# ---------------------------------------------------------------------
298304
# PHASE C: Decode Latents (VAE Decoder)

0 commit comments

Comments
 (0)