Download tests/utils/gpu_tests/test_seqlen_balancing.py from LiangYan3612/guage: direct link, hf CLI and curl.
- Browser
- Download file 4.41 kB
-
https://huggingface.co/LiangYan3612/guage/resolve/main/tests/utils/gpu_tests/test_seqlen_balancing.py
- Command line
-
hf download hf://LiangYan3612/guage/tests/utils/gpu_tests/test_seqlen_balancing.py
-
curl -L -o test_seqlen_balancing.py https://huggingface.co/LiangYan3612/guage/resolve/main/tests/utils/gpu_tests/test_seqlen_balancing.py
4.41 kB
| # Copyright 2025 Bytedance Ltd. and/or its affiliates | |
| # | |
| # Licensed under the Apache License, Version 2.0 (the "License"); | |
| # you may not use this file except in compliance with the License. | |
| # You may obtain a copy of the License at | |
| # | |
| # http://www.apache.org/licenses/LICENSE-2.0 | |
| # | |
| # Unless required by applicable law or agreed to in writing, software | |
| # distributed under the License is distributed on an "AS IS" BASIS, | |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| # See the License for the specific language governing permissions and | |
| # limitations under the License. | |
| import torch | |
| import torch.distributed as dist | |
| import torch.multiprocessing as mp | |
| from verl import DataProto | |
| from verl.utils.model import create_random_mask | |
| from verl.utils.seqlen_balancing import ceildiv, get_reverse_idx, rearrange_micro_batches | |
| def test_seqlen_balancing(): | |
| input_ids = torch.randint(low=0, high=10, size=(20, 100)) | |
| attention_mask = create_random_mask(input_ids=input_ids, max_ratio_of_left_padding=0.1, max_ratio_of_valid_token=0.9, min_ratio_of_valid_token=0.5) | |
| data = {"input_ids": input_ids, "attention_mask": attention_mask} | |
| dataproto = DataProto.from_single_dict(data) | |
| micro_batches, micro_bsz_idx_lst = rearrange_micro_batches(dataproto.batch, max_token_len=300) | |
| batch = torch.cat(micro_batches) | |
| micro_bsz_idx = [] | |
| for idx in micro_bsz_idx_lst: | |
| micro_bsz_idx.extend(idx) | |
| reverse_idx_map = get_reverse_idx(micro_bsz_idx) | |
| reverse_idx_map = torch.tensor(reverse_idx_map) | |
| new_batch = batch[reverse_idx_map] | |
| torch.testing.assert_close(new_batch, dataproto.batch) | |
| def _worker(rank, world_size, init_method, max_token_len, use_same_dp, min_mb): | |
| # 1) init process group & CUDA | |
| torch.cuda.set_device(rank) | |
| dist.init_process_group( | |
| backend="nccl", | |
| init_method=init_method, | |
| world_size=world_size, | |
| rank=rank, | |
| ) | |
| # 2) build a small random batch (each rank different length to force mismatch) | |
| torch.manual_seed(42 + rank) | |
| input_ids = torch.randint(0, 10, (20 + rank * 5, 100), device=f"cuda:{rank}") | |
| attention_mask = create_random_mask( | |
| input_ids=input_ids, | |
| max_ratio_of_left_padding=0.1, | |
| max_ratio_of_valid_token=0.9, | |
| min_ratio_of_valid_token=0.5, | |
| ) | |
| dp = {"input_ids": input_ids, "attention_mask": attention_mask} | |
| proto = DataProto.from_single_dict(dp) | |
| batch = proto.batch | |
| # 3) call rearrange_micro_batches with one of the two params under test | |
| micros, idx_lst = rearrange_micro_batches( | |
| batch, | |
| max_token_len=max_token_len, | |
| dp_group=dist.group.WORLD, | |
| same_micro_num_in_dp=use_same_dp, | |
| min_num_micro_batch=min_mb, | |
| ) | |
| # 4) check the enforced counts | |
| seq_len_effective: torch.Tensor = batch["attention_mask"].sum(dim=1) | |
| total_seqlen = seq_len_effective.sum().item() | |
| local = min(len(seq_len_effective), ceildiv(total_seqlen, max_token_len)) | |
| if min_mb is not None: | |
| expected = max(local, min_mb) | |
| assert len(micros) == expected | |
| if use_same_dp: | |
| # gather all local_counts | |
| counts = [torch.zeros(1, device=f"cuda:{rank}") for _ in range(world_size)] | |
| counts[rank].fill_(local) | |
| dist.all_gather(counts, counts[rank]) | |
| expected = max(int(c.item()) for c in counts) | |
| assert len(micros) == expected | |
| else: | |
| # if neither, we get the local natural count | |
| assert len(micros) == local | |
| # 5) reconstruction sanity: concat→reverse_idx→orig | |
| flat = torch.cat(micros, dim=0) | |
| idx = [] | |
| for sub in idx_lst: | |
| idx.extend(sub) | |
| inv = get_reverse_idx(idx) | |
| inv = torch.tensor(inv, device=flat.device) | |
| reconstructed = flat[inv] | |
| torch.testing.assert_close(reconstructed, batch) | |
| dist.destroy_process_group() | |
| def test_seqlen_balancing_distributed_params(tmp_path): | |
| world_size = 2 | |
| init_file = tmp_path / "dist_init" | |
| init_file.write_text("") # empty file | |
| init_method = f"file://{init_file}" | |
| # test min_num_micro_batch only | |
| mp.spawn( | |
| _worker, | |
| args=(world_size, init_method, 300, False, 4), | |
| nprocs=world_size, | |
| join=True, | |
| ) | |
| # test same_micro_num_in_dp only | |
| mp.spawn( | |
| _worker, | |
| args=(world_size, init_method, 300, True, None), | |
| nprocs=world_size, | |
| join=True, | |
| ) | |