|
11 | 11 | import torch.distributed as dist |
12 | 12 | from transformer_engine.pytorch.attention.dot_product_attention.context_parallel import ( |
13 | 13 | get_cu_seqlens_on_cp_rank, |
| 14 | + get_thd_partitioned_indices, |
14 | 15 | ) |
15 | 16 | from transformer_engine.pytorch.attention.dot_product_attention.utils import combine_and_quantize |
16 | | -import transformer_engine_torch as tex |
17 | 17 | from transformer_engine.pytorch import DType |
18 | 18 | from test_attention_with_cp import ( |
19 | 19 | model_configs_flash_attn, |
20 | 20 | model_configs_fused_attn, |
21 | 21 | ) |
22 | 22 | from transformer_engine.pytorch import ( |
23 | 23 | autocast, |
| 24 | + CPLoadBalancingStrategy, |
24 | 25 | DotProductAttention, |
25 | 26 | Float8Quantizer, |
26 | 27 | Float8CurrentScalingQuantizer, |
@@ -52,6 +53,7 @@ def generate_input_shapes( |
52 | 53 | world_size: int, |
53 | 54 | kernel_backend: str, |
54 | 55 | fa_pad_between_seqs: str = "False", |
| 56 | + load_balancing_strategy=CPLoadBalancingStrategy.DUAL_CHUNK_SWAP, |
55 | 57 | ): |
56 | 58 | if qkv_format == "bshd": |
57 | 59 | q_input_shape = ( |
@@ -110,8 +112,33 @@ def generate_input_shapes( |
110 | 112 | cu_seqlens_q_padded = None |
111 | 113 | cu_seqlens_kv_padded = None |
112 | 114 | elif qkv_format == "thd": |
113 | | - seqlens_q = torch.randint(0, config.max_seqlen_q + 1, [config.batch_size]).to(torch.int32) |
114 | | - seqlens_q_padded = (seqlens_q + 2 * world_size - 1) // (world_size * 2) * (world_size * 2) |
| 115 | + if load_balancing_strategy is CPLoadBalancingStrategy.NO_LOAD_BALANCE: |
| 116 | + assert config.batch_size == 2 |
| 117 | + if kernel_backend == "FlashAttention" and fa_pad_between_seqs == "False": |
| 118 | + seqlens_q = torch.tensor( |
| 119 | + [config.max_seqlen_q - 2, config.max_seqlen_q], dtype=torch.int32 |
| 120 | + ) |
| 121 | + assert seqlens_q.sum().item() % world_size == 0 |
| 122 | + seqlens_q_padded = seqlens_q |
| 123 | + else: |
| 124 | + # Exercise both document padding and a CP chunk boundary inside a document. |
| 125 | + seqlens_q = torch.tensor( |
| 126 | + [config.max_seqlen_q - 2, config.max_seqlen_q - 1], |
| 127 | + dtype=torch.int32, |
| 128 | + ) |
| 129 | + padded_total = 2 * config.max_seqlen_q |
| 130 | + assert padded_total % world_size == 0 |
| 131 | + seqlens_q_padded = torch.tensor( |
| 132 | + [config.max_seqlen_q - 1, config.max_seqlen_q + 1], |
| 133 | + dtype=torch.int32, |
| 134 | + ) |
| 135 | + else: |
| 136 | + seqlens_q = torch.randint(0, config.max_seqlen_q + 1, [config.batch_size]).to( |
| 137 | + torch.int32 |
| 138 | + ) |
| 139 | + seqlens_q_padded = ( |
| 140 | + (seqlens_q + 2 * world_size - 1) // (world_size * 2) * (world_size * 2) |
| 141 | + ) |
115 | 142 | cu_seqlens_q_padded = torch.cat( |
116 | 143 | [ |
117 | 144 | torch.zeros([1], dtype=torch.int32), |
@@ -206,10 +233,12 @@ def run_dpa_with_cp( |
206 | 233 | is_training="True", |
207 | 234 | fa_pad_between_seqs="False", |
208 | 235 | deterministic="False", |
| 236 | + load_balancing_strategy="DUAL_CHUNK_SWAP", |
209 | 237 | log_level=logging.WARNING, |
210 | 238 | ): |
211 | 239 | """Test DotProductAttention module with context parallelism""" |
212 | 240 | logging.root.setLevel(log_level) |
| 241 | + load_balancing_strategy = CPLoadBalancingStrategy[load_balancing_strategy] |
213 | 242 | # When is_training is False, gradient outputs are None. |
214 | 243 | is_training = is_training == "True" |
215 | 244 | pad_between_seqs = None |
@@ -331,7 +360,14 @@ def run_dpa_with_cp( |
331 | 360 | cu_seqlens_kv, |
332 | 361 | cu_seqlens_q_padded, |
333 | 362 | cu_seqlens_kv_padded, |
334 | | - ) = generate_input_shapes(qkv_format, config, world_size, kernel_backend, fa_pad_between_seqs) |
| 363 | + ) = generate_input_shapes( |
| 364 | + qkv_format, |
| 365 | + config, |
| 366 | + world_size, |
| 367 | + kernel_backend, |
| 368 | + fa_pad_between_seqs, |
| 369 | + load_balancing_strategy, |
| 370 | + ) |
335 | 371 | q_orig = torch.clamp(torch.randn(q_input_shape, dtype=dtypes[dtype]), min=-1, max=1).cuda() |
336 | 372 | k_orig = torch.clamp(torch.randn(k_input_shape, dtype=dtypes[dtype]), min=-1, max=1).cuda() |
337 | 373 | v_orig = torch.clamp(torch.randn(v_input_shape, dtype=dtypes[dtype]), min=-1, max=1).cuda() |
@@ -468,11 +504,21 @@ def run_dpa_with_cp( |
468 | 504 | x.view(*x.shape[:seq_dim], -1, *x.shape[(seq_dim + 2) :]) for x in [q_, k_, v_, dout_] |
469 | 505 | ] |
470 | 506 | elif qkv_format == "thd": |
471 | | - seq_idx_q = tex.thd_get_partitioned_indices( |
472 | | - cu_seqlens_q_padded, q_.shape[0], world_size, rank |
| 507 | + seq_idx_q = get_thd_partitioned_indices( |
| 508 | + cu_seqlens_q_padded, |
| 509 | + q_.shape[0], |
| 510 | + world_size, |
| 511 | + rank, |
| 512 | + device=q_.device, |
| 513 | + load_balancing_strategy=load_balancing_strategy, |
473 | 514 | ) |
474 | | - seq_idx_kv = tex.thd_get_partitioned_indices( |
475 | | - cu_seqlens_kv_padded, k_.shape[0], world_size, rank |
| 515 | + seq_idx_kv = get_thd_partitioned_indices( |
| 516 | + cu_seqlens_kv_padded, |
| 517 | + k_.shape[0], |
| 518 | + world_size, |
| 519 | + rank, |
| 520 | + device=k_.device, |
| 521 | + load_balancing_strategy=load_balancing_strategy, |
476 | 522 | ) |
477 | 523 | q_, dout_ = [x.index_select(0, seq_idx_q) for x in [q_, dout_]] |
478 | 524 | k_, v_ = [x.index_select(0, seq_idx_kv) for x in [k_, v_]] |
@@ -516,6 +562,7 @@ def run_dpa_with_cp( |
516 | 562 | cp_comm_ranks, |
517 | 563 | torch.cuda.Stream(), |
518 | 564 | cp_comm_type, |
| 565 | + load_balancing_strategy, |
519 | 566 | ) |
520 | 567 | if config.softmax_type != "vanilla": |
521 | 568 | core_attn.softmax_offset.grad.zero_() |
|
0 commit comments