# ParaAttention

    

    

Büyük görsel ve video üretim modelleri, örneğin [FLUX.1-dev](https://huggingface.co/black-forest-labs/FLUX.1-dev) ve [HunyuanVideo](https://huggingface.co/tencent/HunyuanVideo), boyutları nedeniyle gerçek zamanlı uygulamalar ve dağıtım için çıkarım (inference) açısından zorluk yaratabilir.

[ParaAttention](https://github.com/chengzeyi/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. Baz çizelge ölçümümüz için herhangi bir optimizasyon uygulanmamıştır; HunyuanVideo için bellek dışı (out-of-memory) hatalarından kaçınmak amacıyla yapılanlar hariç.

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.

> [!TIP]
> 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.

    
    AdaCache nasıl çalışır, First Block Cache bunun bir varyantıdır

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.

```python
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 | ![Original](https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/para-attn/flux-original.png) | ![FBCache rdt=0.06](https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/para-attn/flux-fbc-0.06.png) | ![FBCache rdt=0.08](https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/para-attn/flux-fbc-0.08.png) | ![FBCache rdt=0.10](https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/para-attn/flux-fbc-0.10.png) | ![FBCache rdt=0.12](https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/para-attn/flux-fbc-0.12.png) |
| 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.

```python
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)
```

  
  Tarayıcınız video etiketini desteklemiyor.

 FBCache olmadan HunyuanVideo

  
  Tarayıcınız video etiketini desteklemiyor.

 FBCache ile HunyuanVideo

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](https://www.nvidia.com/en-us/data-center/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](https://github.com/pytorch/ao/tree/main) paketini yükleyin.

```bash
pip3 install -U torch torchao
```

[torch.compile](https://pytorch.org/tutorials/intermediate/torch_compile_tutorial.html), `mode="max-autotune-no-cudagraphs"` veya `mode="max-autotune"` parametresiyle kullanıldığında, performans için en iyi ç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.

> [!TIP]
> 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_threshold` değerini daha büyük bir değere değiştirmeniz gerekir.

```python
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.

```python
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 hesaplamalarıyla darboğaza takılır. Bu modeller, kuantizasyondan 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.

> [!TIP]
> Birden fazla GPU ile çıkarımı ölçeklendirmenin ayrıntılı talimatları ve örnekleri için [ParaAttention](https://github.com/chengzeyi/ParaAttention/tree/main) 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](https://pytorch.org/docs/stable/multiprocessing.html) 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.

```python
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](https://pytorch.org/docs/stable/elastic/run.html) ile başlatın.

```bash
# Use --nproc_per_node to specify the number of GPUs
torchrun --nproc_per_node=2 run_flux.py
```

2 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.

```python
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](https://pytorch.org/docs/stable/elastic/run.html) ile başlatın.

```bash
# Use --nproc_per_node to specify the number of GPUs
torchrun --nproc_per_node=8 run_hunyuan_video.py
```

8 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 |

