Diffusers documentation
ParaAttention
ParaAttention


Large image and video generation models, such as FLUX.1-dev and HunyuanVideo, can be an inference challenge for real-time applications and deployment because of their size.
ParaAttention, bağlam paralelliği ve ilk blok önbelleği uygulayan ve çıkarımı hızlandırmak için diğer tekniklerle (torch.compile, fp8 dinamik kuantizasyonu) birleştirilebilen bir kütüphanedir.
Bu kılavuz, ParaAttention'ın NVIDIA L20 GPU'larında FLUX.1-dev ve HunyuanVideo'ya nasıl uygulanacağını gösterecektir. HunyuanVideo için bellek dışı (out-of-memory) hatalarını önlemek dışında, temel (baseline) kıyaslama ölçümümüz için herhangi bir optimizasyon uygulanmamıştır.
Temel performans ölçümümüz, FLUX.1-dev'in 28 adımda 1024x1024 çözünürlüğünde bir görüntüyü 26,36 saniyede, HunyuanVideo'nun ise 30 adımda 720p çözünürlüğünde 129 kareyi 3675,71 saniyede üretebildiğini göstermektedir.
Bağlam paralelliği ile daha hızlı çıkarım için, özellikle çok sayıda GPU varsa, NVLink desteğine sahip NVIDIA A100 veya H100 GPU'ları (müsaitse) kullanmayı deneyin.
İlk Blok Önbelleği
Modeldeki transformers bloklarının çıktılarını önbelleğe alıp sonraki çıkarım adımlarında yeniden kullanmak, hesaplama maliyetini azaltır ve çıkarımı hızlandırır.
Ancak, üretilen görüntü veya videoların kalitesini garanti altına almak için önbelleğin ne zaman yeniden kullanılacağına karar vermek zordur. ParaAttention, model çıktılar arasındaki farkı yaklaşık olarak hesaplamak için doğrudan ilk transformer bloğu çıktısının kalan farkını kullanır. Fark yeterince küçük olduğunda, önceki çıkarım adımlarının kalan farkı yeniden kullanılır. Başka bir deyişle, gürültü azaltma adımı atlanır.
Bu, FLUX.1-dev ve HunyuanVideo çıkarımında çok iyi kaliteyle 2x hızlanma sağlar.
FLUX.1-dev üzerinde ilk blok önbelleğini uygulamak için aşağıda gösterildiği gibi apply_cache_on_pipe fonksiyonunu çağırın. 0.08, FLUX modelleri için varsayılan artıksal fark değeridir.
import time
import torch
from diffusers import FluxPipeline
pipe = FluxPipeline.from_pretrained(
"black-forest-labs/FLUX.1-dev",
dtype=torch.bfloat16,
).to("cuda") # or "mps", "xpu", "cpu"
from para_attn.first_block_cache.diffusers_adapters import apply_cache_on_pipe
apply_cache_on_pipe(pipe, residual_diff_threshold=0.08)
# Enable memory savings
# pipe.enable_model_cpu_offload()
# pipe.enable_sequential_cpu_offload()
begin = time.time()
image = pipe(
"A cat holding a sign that says hello world",
num_inference_steps=28,
).images[0]
end = time.time()
print(f"Time: {end - begin:.2f}s")
print("Saving image to flux.png")
image.save("flux.png")| Optimizasyonlar | Orijinal | FBCache rdt=0.06 | FBCache rdt=0.08 | FBCache rdt=0.10 | FBCache rdt=0.12 |
|---|---|---|---|---|---|
| Önizleme | ![]() | ![]() | ![]() | ![]() | ![]() |
| Duvar Saati (s) | 26.36 | 21.83 | 17.01 | 16.00 | 13.78 |
First Block Cache, neredeyse sıfır kalite kaybı ile birlikte, referans değerine kıyasla çıkarım hızını 17,01 saniyeye düşürerek 1,55 kat daha hızlı bir performans sağladı.
HunyuanVideo üzerinde First Block Cache uygulamak için aşağıda gösterildiği gibi apply_cache_on_pipe kullanın. 0.06, HunyuanVideo modelleri için varsayılan kalan fark değeridir.
import time
import torch
from diffusers import HunyuanVideoPipeline, HunyuanVideoTransformer3DModel
from diffusers.utils import export_to_video
model_id = "tencent/HunyuanVideo"
transformer = HunyuanVideoTransformer3DModel.from_pretrained(
model_id,
subfolder="transformer",
dtype=torch.bfloat16,
revision="refs/pr/18",
)
pipe = HunyuanVideoPipeline.from_pretrained(
model_id,
transformer=transformer,
dtype=torch.float16,
revision="refs/pr/18",
).to("cuda") # or "mps", "xpu", "cpu"
from para_attn.first_block_cache.diffusers_adapters import apply_cache_on_pipe
apply_cache_on_pipe(pipe, residual_diff_threshold=0.6)
pipe.vae.enable_tiling()
begin = time.time()
output = pipe(
prompt="A cat walks on the grass, realistic",
height=720,
width=1280,
num_frames=129,
num_inference_steps=30,
).frames[0]
end = time.time()
print(f"Time: {end - begin:.2f}s")
print("Saving video to hunyuan_video.mp4")
export_to_video(output, "hunyuan_video.mp4", fps=15)First Block Cache, neredeyse sıfır kalite kaybı ile birlikte, referans değerine kıyasla çıkarım süresini 2271.06 saniyeye indirerek 1.62x daha hızlı bir performans sağladı.
fp8 kuantizasyonu
Dinamik kuantizasyon ile fp8, çıkarımı daha da hızlandırır ve bellek kullanımını azaltır. 8-bit NVIDIA Tensor Cores'ün kullanılabilmesi için hem aktivasyonların hem de ağırlıkların kuantize edilmesi gerekir.
Metin kodlayıcı ve dönüştürücü modelini nicelleştirmek için float8_weight_only ve float8_dynamic_activation_float8_weight kullanın.
Varsayılan kuantizasyon yöntemi tensör başına kuantizasyondur, ancak GPU'nuz satır başına kuantizasyonu destekliyorsa daha iyi doğruluk için bunu da deneyebilirsiniz.
Aşağıdaki komutla torchao paketini yükleyin.
pip3 install -U torch torchao
torch.compile, mode="max-autotune-no-cudagraphs" veya mode="max-autotune" ile çağrıldığında performansı en iyi sağlayacak çekirdeği seçer. Model ilk kez çağrılıyorsa derleme işlemi uzun sürebilir ancak model derlendikten sonra bu süre kendini amorti eder.
Bu örnek yalnızca dönüştürücü modelini kuantize eder, ancak bellek kullanımını daha da azaltmak için metin kodlayıcısını da kuantize edebilirsiniz.
Dinamik kuantizasyon, model çıktısının dağılımını önemli ölçüde değiştirebilir; bu nedenle etkin olması için
residual_diff_thresholddeğerini daha büyük bir değere değiştirmeniz gerekir.
import time
import torch
from diffusers import FluxPipeline
pipe = FluxPipeline.from_pretrained(
"black-forest-labs/FLUX.1-dev",
dtype=torch.bfloat16,
).to("cuda") # or "mps", "xpu", "cpu"
from para_attn.first_block_cache.diffusers_adapters import apply_cache_on_pipe
apply_cache_on_pipe(
pipe,
residual_diff_threshold=0.12, # Use a larger value to make the cache take effect
)
from torchao.quantization import quantize_, float8_dynamic_activation_float8_weight, float8_weight_only
quantize_(pipe.text_encoder, float8_weight_only())
quantize_(pipe.transformer, float8_dynamic_activation_float8_weight())
pipe.transformer = torch.compile(
pipe.transformer, mode="max-autotune-no-cudagraphs",
)
# Enable memory savings
# pipe.enable_model_cpu_offload()
# pipe.enable_sequential_cpu_offload()
for i in range(2):
begin = time.time()
image = pipe(
"A cat holding a sign that says hello world",
num_inference_steps=28,
).images[0]
end = time.time()
if i == 0:
print(f"Warm up time: {end - begin:.2f}s")
else:
print(f"Time: {end - begin:.2f}s")
print("Saving image to flux.png")
image.save("flux.png")fp8 dinamik kuantizasyonu ve torch.compile, referans değerine kıyasla çıkarım süresini 7,56 saniyeye düşürerek 3,48 kat daha hızlı hale getirdi.
import time
import torch
from diffusers import HunyuanVideoPipeline, HunyuanVideoTransformer3DModel
from diffusers.utils import export_to_video
model_id = "tencent/HunyuanVideo"
transformer = HunyuanVideoTransformer3DModel.from_pretrained(
model_id,
subfolder="transformer",
dtype=torch.bfloat16,
revision="refs/pr/18",
)
pipe = HunyuanVideoPipeline.from_pretrained(
model_id,
transformer=transformer,
dtype=torch.float16,
revision="refs/pr/18",
).to("cuda") # or "mps", "xpu", "cpu"
from para_attn.first_block_cache.diffusers_adapters import apply_cache_on_pipe
apply_cache_on_pipe(pipe)
from torchao.quantization import quantize_, float8_dynamic_activation_float8_weight, float8_weight_only
quantize_(pipe.text_encoder, float8_weight_only())
quantize_(pipe.transformer, float8_dynamic_activation_float8_weight())
pipe.transformer = torch.compile(
pipe.transformer, mode="max-autotune-no-cudagraphs",
)
# Enable memory savings
pipe.vae.enable_tiling()
# pipe.enable_model_cpu_offload()
# pipe.enable_sequential_cpu_offload()
for i in range(2):
begin = time.time()
output = pipe(
prompt="A cat walks on the grass, realistic",
height=720,
width=1280,
num_frames=129,
num_inference_steps=1 if i == 0 else 30,
).frames[0]
end = time.time()
if i == 0:
print(f"Warm up time: {end - begin:.2f}s")
else:
print(f"Time: {end - begin:.2f}s")
print("Saving video to hunyuan_video.mp4")
export_to_video(output, "hunyuan_video.mp4", fps=15)NVIDIA L20 GPU yalnızca 48GB belleğe sahiptir ve HunyuanVideo, yüksek çözünürlük ve yüksek kare sayısı ile çalışırken çok büyük aktivasyon tensörlerine sahip olduğu için derleme sonrasında ve enable_model_cpu_offload çağrılmadığında bellek yetersizliği (OOM) hatalarıyla karşılaşabilir. 80GB’tan az belleğe sahip GPU’larda OOM hatalarını önlemek için çözünürlüğü ve kare sayısını azaltmayı deneyebilirsiniz.
Büyük video üretim modelleri genellikle tam bağlı katmanlardan ziyade dikkat (attention) hesaplamalarıyla darboğaza takılır. Bu modeller, kuantizasyon ve torch.compile’den önemli ölçüde fayda sağlamaz.
Bağlam Paralelliği
Context Parallelism, çıkarımı paralelleştirir ve birden çok GPU ile ölçeklenir. ParaAttention bileşen tasarımı, Context Parallelism'i First Block Cache ve dinamik kuantizasyonla birleştirmenizi sağlar.
Birden fazla GPU ile çıkarımı ölçeklendirmenin ayrıntılı talimatları ve örnekleri için ParaAttention deposuna başvurun.
Eğer çıkarım sürecinin kalıcı ve hizmet verebilir olması gerekiyorsa, kendi çıkarım işleyicinizi yazmak için torch.multiprocessing kullanmanız önerilir. Bu, süreci başlatma ve modeli yükleme ile yeniden derleme işlemlerinin getirdiği ek yükü ortadan kaldırabilir.
Aşağıdaki kod örneği, en hızlı çıkarım hızı için First Block Cache, fp8 dinamik kuantizasyonu, torch.compile ve Context Parallelism'i birleştirir.
import time
import torch
import torch.distributed as dist
from diffusers import FluxPipeline
dist.init_process_group()
torch.cuda.set_device(dist.get_rank())
pipe = FluxPipeline.from_pretrained(
"black-forest-labs/FLUX.1-dev",
dtype=torch.bfloat16,
).to("cuda") # or "mps", "xpu", "cpu"
from para_attn.context_parallel import init_context_parallel_mesh
from para_attn.context_parallel.diffusers_adapters import parallelize_pipe
from para_attn.parallel_vae.diffusers_adapters import parallelize_vae
mesh = init_context_parallel_mesh(
pipe.device.type,
max_ring_dim_size=2,
)
parallelize_pipe(
pipe,
mesh=mesh,
)
parallelize_vae(pipe.vae, mesh=mesh._flatten())
from para_attn.first_block_cache.diffusers_adapters import apply_cache_on_pipe
apply_cache_on_pipe(
pipe,
residual_diff_threshold=0.12, # Use a larger value to make the cache take effect
)
from torchao.quantization import quantize_, float8_dynamic_activation_float8_weight, float8_weight_only
quantize_(pipe.text_encoder, float8_weight_only())
quantize_(pipe.transformer, float8_dynamic_activation_float8_weight())
torch._inductor.config.reorder_for_compute_comm_overlap = True
pipe.transformer = torch.compile(
pipe.transformer, mode="max-autotune-no-cudagraphs",
)
# Enable memory savings
# pipe.enable_model_cpu_offload(gpu_id=dist.get_rank())
# pipe.enable_sequential_cpu_offload(gpu_id=dist.get_rank())
for i in range(2):
begin = time.time()
image = pipe(
"A cat holding a sign that says hello world",
num_inference_steps=28,
output_type="pil" if dist.get_rank() == 0 else "pt",
).images[0]
end = time.time()
if dist.get_rank() == 0:
if i == 0:
print(f"Warm up time: {end - begin:.2f}s")
else:
print(f"Time: {end - begin:.2f}s")
if dist.get_rank() == 0:
print("Saving image to flux.png")
image.save("flux.png")
dist.destroy_process_group()run_flux.py dosyasına kaydedin ve torchrun ile başlatın.
# Use --nproc_per_node to specify the number of GPUs
torchrun --nproc_per_node=2 run_flux.py2 NVIDIA L20 GPU ile çıkarım hızı, baz çizgiye kıyasla 8,20 saniyeye düşer; bu da 3,21 kat daha hızlı demektir. 4 L20 ile çıkarım hızı 3,90 saniyedir, yani 6,75 kat daha hızlıdır.
Aşağıdaki kod örneği, en hızlı çıkarım hızı için First Block Cache ve Context Parallelism'i birleştirir.
import time
import torch
import torch.distributed as dist
from diffusers import HunyuanVideoPipeline, HunyuanVideoTransformer3DModel
from diffusers.utils import export_to_video
dist.init_process_group()
torch.cuda.set_device(dist.get_rank())
model_id = "tencent/HunyuanVideo"
transformer = HunyuanVideoTransformer3DModel.from_pretrained(
model_id,
subfolder="transformer",
dtype=torch.bfloat16,
revision="refs/pr/18",
)
pipe = HunyuanVideoPipeline.from_pretrained(
model_id,
transformer=transformer,
dtype=torch.float16,
revision="refs/pr/18",
).to("cuda") # or "mps", "xpu", "cpu"
from para_attn.context_parallel import init_context_parallel_mesh
from para_attn.context_parallel.diffusers_adapters import parallelize_pipe
from para_attn.parallel_vae.diffusers_adapters import parallelize_vae
mesh = init_context_parallel_mesh(
pipe.device.type,
)
parallelize_pipe(
pipe,
mesh=mesh,
)
parallelize_vae(pipe.vae, mesh=mesh._flatten())
from para_attn.first_block_cache.diffusers_adapters import apply_cache_on_pipe
apply_cache_on_pipe(pipe)
# from torchao.quantization import quantize_, float8_dynamic_activation_float8_weight, float8_weight_only
#
# torch._inductor.config.reorder_for_compute_comm_overlap = True
#
# quantize_(pipe.text_encoder, float8_weight_only())
# quantize_(pipe.transformer, float8_dynamic_activation_float8_weight())
# pipe.transformer = torch.compile(
# pipe.transformer, mode="max-autotune-no-cudagraphs",
# )
# Enable memory savings
pipe.vae.enable_tiling()
# pipe.enable_model_cpu_offload(gpu_id=dist.get_rank())
# pipe.enable_sequential_cpu_offload(gpu_id=dist.get_rank())
for i in range(2):
begin = time.time()
output = pipe(
prompt="A cat walks on the grass, realistic",
height=720,
width=1280,
num_frames=129,
num_inference_steps=1 if i == 0 else 30,
output_type="pil" if dist.get_rank() == 0 else "pt",
).frames[0]
end = time.time()
if dist.get_rank() == 0:
if i == 0:
print(f"Warm up time: {end - begin:.2f}s")
else:
print(f"Time: {end - begin:.2f}s")
if dist.get_rank() == 0:
print("Saving video to hunyuan_video.mp4")
export_to_video(output, "hunyuan_video.mp4", fps=15)
dist.destroy_process_group()run_hunyuan_video.py dosyasına kaydedin ve torchrun ile başlatın.
# Use --nproc_per_node to specify the number of GPUs
torchrun --nproc_per_node=8 run_hunyuan_video.py8 NVIDIA L20 GPU ile çıkarım hızı, baz çizgiye kıyasla 649,23 saniyeye düşürülmüş olup 5,66 kat daha hızlıdır.
Benchmarks
| GPU Türü | GPU sayısı | Optimizasyonlar | Duvar Saati (s) | Hızlanma |
|---|---|---|---|---|
| NVIDIA L20 | 1 | Taban | 26.36 | 1.00x |
| NVIDIA L20 | 1 | FBCache (rdt=0.08) | 17.01 | 1.55x |
| NVIDIA L20 | 1 | FP8 DQ | 13.40 | 1.96x |
| NVIDIA L20 | 1 | FBCache (rdt=0.12) + FP8 DQ | 7.56 | 3.48x |
| NVIDIA L20 | 2 | FBCache (rdt=0.12) + FP8 DQ + CP | 4.92 | 5.35x |
| NVIDIA L20 | 4 | FBCache (rdt=0.12) + FP8 DQ + CP | 3.90 | 6.75x |
| GPU Türü | GPU sayısı | Optimizasyonlar | Duvar Saati (s) | Hızlanma |
|---|---|---|---|---|
| NVIDIA L20 | 1 | Taban | 3675.71 | 1.00x |
| NVIDIA L20 | 1 | FBCache | 2271.06 | 1.62x |
| NVIDIA L20 | 2 | FBCache + CP | 1132.90 | 3.24x |
| NVIDIA L20 | 4 | FBCache + CP | 718.15 | 5.12x |
| NVIDIA L20 | 8 | FBCache + CP | 649.23 | 5.66x |




