|
1 | 1 | # Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved. |
2 | 2 |
|
3 | 3 | import dataclasses |
4 | | -import math |
5 | 4 |
|
6 | 5 | import pytest |
7 | 6 | import torch |
8 | 7 |
|
9 | 8 | from megatron.core import config, parallel_state |
10 | | -from megatron.core.models.gpt.gpt_layer_specs import ( |
11 | | - get_gpt_layer_local_submodules, |
12 | | - get_gpt_layer_with_transformer_engine_spec, |
13 | | -) |
14 | | -from megatron.core.packed_seq_params import PackedSeqParams |
| 9 | +from megatron.core.models.gpt.gpt_layer_specs import get_gpt_layer_local_submodules |
15 | 10 | from megatron.core.transformer.moe.fused_a2a import reset_hybrid_ep_buffer |
16 | 11 | from megatron.core.transformer.moe.moe_layer import MoELayer, MoESubmodules |
17 | 12 | from megatron.core.transformer.moe.moe_utils import get_capacity |
18 | 13 | from megatron.core.transformer.spec_utils import get_submodules |
19 | | -from megatron.core.transformer.transformer_block import TransformerBlock |
20 | 14 | from megatron.core.transformer.transformer_config import TransformerConfig |
21 | 15 | from megatron.core.typed_torch import apply_module |
22 | 16 | from megatron.core.utils import is_te_min_version |
@@ -425,173 +419,6 @@ def is_hybrid_ep_available(): |
425 | 419 | return HAVE_HYBRIDEP |
426 | 420 |
|
427 | 421 |
|
428 | | -def _round_up(value, divisor): |
429 | | - return value if divisor <= 1 else (value + divisor - 1) // divisor * divisor |
430 | | - |
431 | | - |
432 | | -def _get_thd_padded_seqlens(seqlens, cp_size, tp_size): |
433 | | - # This follows the runtime packed-sequence path used by the Moonlight script: |
434 | | - # per-sequence lengths must be CP partitionable, and the packed token count |
435 | | - # must be even for TP/SP slicing. |
436 | | - cp_divisor = 2 * cp_size if cp_size > 1 else 1 |
437 | | - padded_seqlens = [_round_up(seqlen, cp_divisor) for seqlen in seqlens] |
438 | | - total_seqlen = sum(padded_seqlens) |
439 | | - total_alignment = math.lcm(cp_divisor, tp_size) |
440 | | - padded_seqlens[-1] += _round_up(total_seqlen, total_alignment) - total_seqlen |
441 | | - return padded_seqlens |
442 | | - |
443 | | - |
444 | | -def _to_cu_seqlens(seqlens): |
445 | | - cu_seqlens = torch.empty(len(seqlens) + 1, dtype=torch.int32, device="cuda") |
446 | | - cu_seqlens[0] = 0 |
447 | | - cu_seqlens[1:] = torch.cumsum(torch.tensor(seqlens, dtype=torch.int32, device="cuda"), dim=0) |
448 | | - return cu_seqlens |
449 | | - |
450 | | - |
451 | | -def _make_thd_packed_seq_params(seqlens, cp_size, tp_size): |
452 | | - padded_seqlens = _get_thd_padded_seqlens(seqlens, cp_size, tp_size) |
453 | | - cu_seqlens_padded = _to_cu_seqlens(padded_seqlens) |
454 | | - max_seqlen = max(padded_seqlens) |
455 | | - # Match get_batch_on_this_rank_for_sequence_packing(): TE consumes padded |
456 | | - # cumulative lengths as both cu_seqlens and cu_seqlens_padded for THD. |
457 | | - return PackedSeqParams( |
458 | | - qkv_format="thd", |
459 | | - cu_seqlens_q=cu_seqlens_padded, |
460 | | - cu_seqlens_kv=cu_seqlens_padded, |
461 | | - cu_seqlens_q_padded=cu_seqlens_padded, |
462 | | - cu_seqlens_kv_padded=cu_seqlens_padded, |
463 | | - max_seqlen_q=max_seqlen, |
464 | | - max_seqlen_kv=max_seqlen, |
465 | | - ) |
466 | | - |
467 | | - |
468 | | -def _make_sharded_thd_hidden_states(seqlens, hidden_size, cp_size, tp_size, dtype): |
469 | | - padded_seqlens = _get_thd_padded_seqlens(seqlens, cp_size, tp_size) |
470 | | - padded_sequences = [] |
471 | | - for seqlen, padded_seqlen in zip(seqlens, padded_seqlens): |
472 | | - sequence = torch.randn(seqlen, hidden_size, device="cuda", dtype=dtype) |
473 | | - if padded_seqlen > seqlen: |
474 | | - sequence = torch.cat( |
475 | | - [ |
476 | | - sequence, |
477 | | - torch.zeros(padded_seqlen - seqlen, hidden_size, device="cuda", dtype=dtype), |
478 | | - ], |
479 | | - dim=0, |
480 | | - ) |
481 | | - padded_sequences.append(sequence) |
482 | | - |
483 | | - hidden_states = torch.cat(padded_sequences, dim=0) |
484 | | - if cp_size > 1: |
485 | | - import transformer_engine_torch as tex |
486 | | - |
487 | | - cu_seqlens_padded = _to_cu_seqlens(padded_seqlens) |
488 | | - cp_rank = parallel_state.get_context_parallel_rank() |
489 | | - index = tex.thd_get_partitioned_indices( |
490 | | - cu_seqlens_padded, hidden_states.shape[0], cp_size, cp_rank |
491 | | - ) |
492 | | - hidden_states = hidden_states.index_select(0, index) |
493 | | - |
494 | | - tp_rank = parallel_state.get_tensor_model_parallel_rank() |
495 | | - sequence_parallel_length = hidden_states.shape[0] // tp_size |
496 | | - hidden_states = hidden_states[ |
497 | | - tp_rank * sequence_parallel_length : (tp_rank + 1) * sequence_parallel_length |
498 | | - ] |
499 | | - return hidden_states.unsqueeze(1).contiguous().requires_grad_(True) |
500 | | - |
501 | | - |
502 | | -@pytest.mark.skipif(not torch.cuda.is_available(), reason="CUDA not available") |
503 | | -@pytest.mark.skipif( |
504 | | - Utils.world_size % 8 != 0, reason="requires world size divisible by 8 for pp2/cp2/tp2/ep2/etp2" |
505 | | -) |
506 | | -@pytest.mark.internal |
507 | | -@pytest.mark.parametrize("dispatcher", ["alltoall", "deepep", "hybridep"]) |
508 | | -def test_sequence_packing_thd_e2e_proxy_model(dispatcher): |
509 | | - """Run packed THD attention + MoE forward/backward with major parallelisms enabled.""" |
510 | | - if not is_te_min_version("2.9.0"): |
511 | | - pytest.skip("SFT sequence packing requires Transformer Engine >= 2.9.0") |
512 | | - if dispatcher == "deepep" and not is_deep_ep_available(): |
513 | | - pytest.skip("Deep EP is not available") |
514 | | - if dispatcher == "hybridep" and not is_hybrid_ep_available(): |
515 | | - pytest.skip("Hybrid EP is not available") |
516 | | - |
517 | | - tp_size, pp_size, cp_size, ep_size, etp_size = 2, 2, 2, 2, 2 |
518 | | - Utils.initialize_model_parallel( |
519 | | - tensor_model_parallel_size=tp_size, |
520 | | - pipeline_model_parallel_size=pp_size, |
521 | | - context_parallel_size=cp_size, |
522 | | - expert_model_parallel_size=ep_size, |
523 | | - expert_tensor_parallel_size=etp_size, |
524 | | - ) |
525 | | - _set_random_seed(seed_=123, data_parallel_random_init=False) |
526 | | - |
527 | | - try: |
528 | | - spec = get_gpt_layer_with_transformer_engine_spec(num_experts=4, moe_grouped_gemm=False) |
529 | | - transformer_config = TransformerConfig( |
530 | | - num_layers=4, |
531 | | - hidden_size=1024, |
532 | | - ffn_hidden_size=2048, |
533 | | - moe_ffn_hidden_size=2048, |
534 | | - num_attention_heads=8, |
535 | | - tensor_model_parallel_size=tp_size, |
536 | | - pipeline_model_parallel_size=pp_size, |
537 | | - context_parallel_size=cp_size, |
538 | | - expert_model_parallel_size=ep_size, |
539 | | - expert_tensor_parallel_size=etp_size, |
540 | | - sequence_parallel=True, |
541 | | - sequence_packing_scheduler="dp_balanced", |
542 | | - max_seqlen_per_dp_cp_rank=1024, |
543 | | - cp_comm_type="p2p", |
544 | | - num_moe_experts=4, |
545 | | - moe_router_topk=2, |
546 | | - moe_router_load_balancing_type="aux_loss", |
547 | | - moe_token_dispatcher_type=( |
548 | | - "flex" if dispatcher in ("deepep", "hybridep") else dispatcher |
549 | | - ), |
550 | | - moe_flex_dispatcher_backend=( |
551 | | - dispatcher if dispatcher in ("deepep", "hybridep") else "deepep" |
552 | | - ), |
553 | | - moe_grouped_gemm=False, |
554 | | - moe_router_dtype="fp32", |
555 | | - params_dtype=torch.bfloat16, |
556 | | - pipeline_dtype=torch.bfloat16, |
557 | | - autocast_dtype=torch.bfloat16, |
558 | | - bf16=True, |
559 | | - add_bias_linear=False, |
560 | | - attention_dropout=0.0, |
561 | | - hidden_dropout=0.0, |
562 | | - use_cpu_initialization=True, |
563 | | - ) |
564 | | - transformer_block = TransformerBlock(transformer_config, spec).cuda().to(torch.bfloat16) |
565 | | - |
566 | | - torch.manual_seed(1000 + torch.distributed.get_rank()) |
567 | | - seqlens = [257, 509, 1021] |
568 | | - hidden_states = _make_sharded_thd_hidden_states( |
569 | | - seqlens, transformer_config.hidden_size, cp_size, tp_size, torch.bfloat16 |
570 | | - ) |
571 | | - packed_seq_params = _make_thd_packed_seq_params(seqlens, cp_size, tp_size) |
572 | | - |
573 | | - output = transformer_block( |
574 | | - hidden_states=hidden_states, attention_mask=None, packed_seq_params=packed_seq_params |
575 | | - ) |
576 | | - assert output.shape == hidden_states.shape |
577 | | - assert torch.isfinite(output).all() |
578 | | - |
579 | | - loss = output.float().square().mean() |
580 | | - loss.backward() |
581 | | - |
582 | | - assert hidden_states.grad is not None |
583 | | - assert hidden_states.grad.shape == hidden_states.shape |
584 | | - assert torch.isfinite(hidden_states.grad).all() |
585 | | - assert any( |
586 | | - param.grad is not None and torch.isfinite(param.grad).all() |
587 | | - for param in transformer_block.parameters() |
588 | | - if param.requires_grad |
589 | | - ) |
590 | | - finally: |
591 | | - reset_hybrid_ep_buffer() |
592 | | - Utils.destroy_model_parallel() |
593 | | - |
594 | | - |
595 | 422 | @pytest.mark.skipif( |
596 | 423 | not is_deep_ep_available() and not is_hybrid_ep_available(), |
597 | 424 | reason="Deep EP and Hybrid EP are not available", |
|
0 commit comments