Skip to content

Commit 58bc4c8

Browse files
lxd-cumtzihugithubclaude
authored
Unified multiple platform support for flagscale training (flagos-ai#1146)
## Integrates with the Megatron-LM-FL multi-platform plugin system Reference: - Platform cuda support, for nvidia chips or other cuda-compatible chips: flagos-ai/Megatron-LM-FL#11 - Platform musa support, for mthreads chips: flagos-ai/Megatron-LM-FL#14 --------- Co-authored-by: zihugithub <fbye@baai.ac.cn> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent bcf923d commit 58bc4c8

37 files changed

Lines changed: 326 additions & 207 deletions

.github/workflows/functional_tests_hetero_train.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ jobs:
166166
${INSTALL_DIR:+--install-dir "$INSTALL_DIR"} \
167167
--no-system --no-dev --no-base --no-task \
168168
--src-deps megatron-lm \
169+
--force-build \
169170
--retry-count 3
170171
171172
# Install FlagScale CLI

.github/workflows/functional_tests_train.yml

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,8 +155,24 @@ jobs:
155155
command -v flagscale || { echo "❌ FlagScale CLI not found in PATH"; exit 1; }
156156
echo "✅ FlagScale CLI installed successfully: $(flagscale --version 2>/dev/null || echo 'version unknown')"
157157
158-
# For train task: all dependencies are pre-installed in the env
159-
# No additional installation needed
158+
# Install Megatron-LM-FL from source (force-build to replace pre-installed megatron-core)
159+
# Derive install-dir from env_path (e.g., /root/miniconda3 -> /root)
160+
INSTALL_DIR=""
161+
if [ "$PKG_MGR" = "conda" ] && [ -n "$ENV_PATH" ]; then
162+
INSTALL_DIR=$(dirname "$ENV_PATH")
163+
fi
164+
165+
./tools/install/install.sh \
166+
--platform ${{ inputs.platform }} \
167+
--task train \
168+
--pkg-mgr "$PKG_MGR" \
169+
${ENV_NAME:+--env-name "$ENV_NAME"} \
170+
${INSTALL_DIR:+--install-dir "$INSTALL_DIR"} \
171+
--no-system --no-dev --no-base --no-task \
172+
--src-deps megatron-lm \
173+
--force-build \
174+
--retry-count 3
175+
160176
echo "Environment ready for train tests"
161177
timeout-minutes: 30
162178

.github/workflows/unit_tests_common.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ jobs:
154154
fi
155155
156156
# Only install Megatron-LM source dep (pip deps are pre-installed in Docker image)
157+
# Use --force-build to ensure Megatron-LM-FL replaces any pre-installed megatron-core
157158
./tools/install/install.sh \
158159
--platform ${{ inputs.platform }} \
159160
--task train \
@@ -163,6 +164,7 @@ jobs:
163164
--no-system --no-dev --no-base --no-task \
164165
--src-deps megatron-lm \
165166
--pip-deps typer \
167+
--force-build \
166168
--retry-count 3
167169
168170
# TODO: temp solution to install newly added deps, remove once the new image is built

flagscale/runner/launcher/launcher_ssh.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,10 @@ def run(
345345
if enable_monitoring is None:
346346
enable_monitoring = self.config.experiment.runner.get("enable_monitoring", False)
347347
num_visible_devices = None
348-
visible_devices = self.user_envs.get("CUDA_VISIBLE_DEVICES", None)
348+
# visible_devices = self.user_envs.get("CUDA_VISIBLE_DEVICES", None)
349+
visible_devices = next(
350+
(v for k, v in self.user_envs.items() if k.endswith("_VISIBLE_DEVICES")), None
351+
)
349352
if visible_devices is not None and isinstance(visible_devices, str):
350353
visible_devices = visible_devices.split(",")
351354
num_visible_devices = len(visible_devices)
@@ -983,7 +986,10 @@ def _run_gpu_health_check(self):
983986

984987
# Get CUDA_VISIBLE_DEVICES if set
985988
cur_envs = add_decive_extra_config(self.user_envs, resource_info["type"])
986-
visible_devices = cur_envs.get("CUDA_VISIBLE_DEVICES", None)
989+
# visible_devices = cur_envs.get("CUDA_VISIBLE_DEVICES", None)
990+
visible_devices = next(
991+
(v for k, v in cur_envs.items() if k.endswith("_VISIBLE_DEVICES")), None
992+
)
987993
num_visible_devices = None
988994
if visible_devices is not None and isinstance(visible_devices, str):
989995
visible_devices = visible_devices.split(",")
@@ -1050,7 +1056,10 @@ def run_health_check_thread(node_config):
10501056
node_rank = 0
10511057
host = "localhost"
10521058

1053-
visible_devices = self.user_envs.get("CUDA_VISIBLE_DEVICES", None)
1059+
# visible_devices = self.user_envs.get("CUDA_VISIBLE_DEVICES", None)
1060+
visible_devices = next(
1061+
(v for k, v in self.user_envs.items() if k.endswith("_VISIBLE_DEVICES")), None
1062+
)
10541063
num_visible_devices = None
10551064
if visible_devices is not None and isinstance(visible_devices, str):
10561065
visible_devices = visible_devices.split(",")

flagscale/train/megatron/legacy/data/biencoder_dataset_utils.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
from megatron.training import get_args, get_tokenizer, print_rank_0
1414
from megatron.training.datasets.data_samplers import MegatronPretrainingSampler
1515

16+
from megatron.plugin.platform import get_platform
17+
cur_platform = get_platform()
18+
1619

1720
def make_attention_mask(source_block, target_block):
1821
"""
@@ -192,7 +195,7 @@ def get_block_samples_mapping(block_dataset, title_dataset, data_prefix, num_epo
192195
# This should be a barrier but nccl barrier assumes
193196
# device_index=rank which is not the case for model
194197
# parallel case
195-
counts = torch.tensor([1], dtype=torch.long, device='cuda')
198+
counts = torch.tensor([1], dtype=torch.long, device=cur_platform.device_name())
196199
torch.distributed.all_reduce(counts, group=mpu.get_data_parallel_group())
197200
assert counts[0].item() == mpu.get_data_parallel_group().size()
198201

flagscale/train/megatron/legacy/data/dataset_utils.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@
3434
from megatron.core import mpu
3535
from megatron.core.datasets.indexed_dataset import IndexedDataset
3636

37+
from megatron.plugin.platform import get_platform
38+
cur_platform = get_platform()
3739

3840
DSET_TYPE_BERT = 'standard_bert'
3941
DSET_TYPE_ICT = 'ict'
@@ -707,7 +709,7 @@ def get_samples_mapping(indexed_dataset,
707709
# This should be a barrier but nccl barrier assumes
708710
# device_index=rank which is not the case for model
709711
# parallel case
710-
counts = torch.tensor([1], dtype=torch.long, device='cuda')
712+
counts = torch.tensor([1], dtype=torch.long, device=cur_platform.device_name())
711713
torch.distributed.all_reduce(counts, group=mpu.get_data_parallel_group())
712714
torch.distributed.all_reduce(counts, group=mpu.get_pipeline_model_parallel_group())
713715
assert counts[0].item() == (

flagscale/train/megatron/legacy/data/realm_dataset_utils.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
from megatron.legacy.data.dataset_utils import create_masked_lm_predictions, pad_and_convert_to_numpy
1111
from megatron.training import get_args, get_tokenizer, print_rank_0
1212

13+
from megatron.plugin.platform import get_platform
14+
cur_platform = get_platform()
15+
1316

1417
def get_one_epoch_dataloader(dataset, micro_batch_size=None):
1518
"""Specifically one epoch to be used in an indexing job."""
@@ -179,7 +182,7 @@ def get_block_samples_mapping(block_dataset, title_dataset, data_prefix, num_epo
179182
# This should be a barrier but nccl barrier assumes
180183
# device_index=rank which is not the case for model
181184
# parallel case
182-
counts = torch.tensor([1], dtype=torch.long, device='cuda')
185+
counts = torch.tensor([1], dtype=torch.long, device=cur_platform.device_name())
183186
torch.distributed.all_reduce(counts, group=mpu.get_data_parallel_group())
184187
assert counts[0].item() == mpu.get_data_parallel_group().size()
185188

flagscale/train/megatron/legacy/fused_kernels/tests/test_fused_kernels.py

Lines changed: 30 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
from megatron.legacy.model.utils import attention_mask_func
1111
from megatron.legacy.fused_kernels import load
1212

13+
from megatron.plugin.platform import get_platform
14+
cur_platform = get_platform()
15+
1316
def test_load_fused_kernels():
1417
try:
1518
import fused_layer_norm_cuda
@@ -23,7 +26,7 @@ def test_load_fused_kernels():
2326
raise e
2427

2528
def test_fused_softmax():
26-
bert = BertModel.from_pretrained("bert-base-cased").cuda().half()
29+
bert = BertModel.from_pretrained("bert-base-cased").to(cur_platform.device()).half()
2730
tokenizer = BertTokenizer.from_pretrained("bert-base-cased")
2831
test_text = (
2932
"Hello. How are you? I am fine thank you and you? yes Good. "
@@ -36,16 +39,16 @@ def test_fused_softmax():
3639
)
3740

3841
embedding_output = bert.embeddings(
39-
input_ids=tokens["input_ids"].cuda(),
42+
input_ids=tokens["input_ids"].to(cur_platform.device()),
4043
position_ids=None,
41-
token_type_ids=tokens["token_type_ids"].cuda(),
44+
token_type_ids=tokens["token_type_ids"].to(cur_platform.device()),
4245
inputs_embeds=None,
4346
past_key_values_length=0,
4447
)
4548

4649
# (bsz, 1, 1, seq_len)
4750
mask = bert.get_extended_attention_mask(
48-
attention_mask=tokens["attention_mask"].cuda(),
51+
attention_mask=tokens["attention_mask"].to(cur_platform.device()),
4952
input_shape=tokens["input_ids"].shape,
5053
device=bert.device,
5154
)
@@ -69,7 +72,7 @@ def test_fused_softmax():
6972
attn_mask_type=AttnMaskType.padding,
7073
scaled_masked_softmax_fusion=True,
7174
)
72-
.cuda()
75+
.to(cur_platform.device())
7376
.half()
7477
)
7578

@@ -88,7 +91,7 @@ def test_fused_softmax():
8891
attn_mask_type=AttnMaskType.padding,
8992
scaled_masked_softmax_fusion=False,
9093
)
91-
.cuda()
94+
.to(cur_platform.device())
9295
.half()
9396
)
9497

@@ -121,7 +124,7 @@ def test_fused_softmax():
121124

122125

123126
def test_fused_upper_triangle_mask_softmax():
124-
gpt = GPT2Model.from_pretrained("gpt2").cuda().half()
127+
gpt = GPT2Model.from_pretrained("gpt2").to(cur_platform.device()).half()
125128
tokenizer = GPT2Tokenizer.from_pretrained("gpt2")
126129
test_text = (
127130
"Hello. How are you? I am fine thank you and you? yes Good. "
@@ -133,14 +136,14 @@ def test_fused_upper_triangle_mask_softmax():
133136
return_tensors="pt",
134137
)
135138

136-
attention_mask = tokens["attention_mask"].cuda()
139+
attention_mask = tokens["attention_mask"].to(cur_platform.device())
137140
attention_mask = attention_mask.view(attention_mask.size(0), -1)
138141
attention_mask = attention_mask[:, None, None, :]
139142
attention_mask = (1.0 - attention_mask) * -10000.0
140143
attention_mask = attention_mask.repeat(1, 1, attention_mask.size()[-1], 1)
141144
attn = gpt.h[0]
142145

143-
hidden_states = gpt.wte(tokens["input_ids"].cuda())
146+
hidden_states = gpt.wte(tokens["input_ids"].to(cur_platform.device()))
144147
q, k, v = attn.attn.c_attn(hidden_states).split(768, dim=-1)
145148
q = attn.attn._split_heads(q, attn.attn.num_heads, attn.attn.head_dim)
146149
k = attn.attn._split_heads(k, attn.attn.num_heads, attn.attn.head_dim)
@@ -169,7 +172,7 @@ def test_fused_upper_triangle_mask_softmax():
169172
attn_mask_type=AttnMaskType.causal,
170173
scaled_masked_softmax_fusion=True,
171174
)
172-
.cuda()
175+
.to(cur_platform.device())
173176
.half()
174177
)
175178

@@ -188,7 +191,7 @@ def test_fused_upper_triangle_mask_softmax():
188191
attn_mask_type=AttnMaskType.causal,
189192
scaled_masked_softmax_fusion=False,
190193
)
191-
.cuda()
194+
.to(cur_platform.device())
192195
.half()
193196
)
194197

@@ -221,7 +224,7 @@ def test_fused_upper_triangle_mask_softmax():
221224

222225

223226
def test_layer_norm():
224-
bert = BertModel.from_pretrained("bert-base-cased").cuda().half()
227+
bert = BertModel.from_pretrained("bert-base-cased").to(cur_platform.device()).half()
225228
tokenizer = BertTokenizer.from_pretrained("bert-base-cased")
226229
test_text = (
227230
"Hello. How are you? I am fine thank you and you? yes Good. "
@@ -236,22 +239,22 @@ def test_layer_norm():
236239
# [bsz, seq_len, d_model]
237240
embedding_output = (
238241
bert.embeddings(
239-
input_ids=tokens["input_ids"].cuda(),
242+
input_ids=tokens["input_ids"].to(cur_platform.device()),
240243
position_ids=None,
241-
token_type_ids=tokens["token_type_ids"].cuda(),
244+
token_type_ids=tokens["token_type_ids"].to(cur_platform.device()),
242245
inputs_embeds=None,
243246
past_key_values_length=0,
244247
)
245-
.cuda()
248+
.to(cur_platform.device())
246249
.half()
247250
)
248251

249252
fused_layernorm_layer = (
250-
MixedFusedLayerNorm(normalized_shape=embedding_output.size(-1)).cuda().half()
253+
MixedFusedLayerNorm(normalized_shape=embedding_output.size(-1)).to(cur_platform.device()).half()
251254
)
252255

253256
torch_layernorm_layer = (
254-
LayerNorm(normalized_shape=embedding_output.size(-1)).cuda().half()
257+
LayerNorm(normalized_shape=embedding_output.size(-1)).to(cur_platform.device()).half()
255258
)
256259

257260
fused_output = fused_layernorm_layer(embedding_output)
@@ -299,8 +302,8 @@ def test_masked_softmax_forward():
299302
scale_t = torch.tensor([1.0])
300303
for qlen in [128, 256, 1024, 2048, 4096]:
301304
for klen in [128, 256, 1024, 2048]:
302-
inputs = torch.normal(0, 2, (batch, attn, qlen, klen), dtype=torch.float16, device='cuda:0')
303-
masks = torch.randint(0, 2, (batch, 1, qlen, klen), dtype=torch.bool, device='cuda:0')
305+
inputs = torch.normal(0, 2, (batch, attn, qlen, klen), dtype=torch.float16, device=cur_platform.device_name(0))
306+
masks = torch.randint(0, 2, (batch, 1, qlen, klen), dtype=torch.bool, device=cur_platform.device_name(0))
304307
softmax_results = scaled_masked_softmax_cuda.forward(inputs, masks, scale_t[0].item())
305308
softmax_results_torch = forward_torch_softmax(inputs, masks, scale_t[0].item())
306309
error = (softmax_results_torch - softmax_results).abs().max()
@@ -314,9 +317,9 @@ def test_masked_softmax_backward():
314317
scale_t = torch.tensor([1.0])
315318
for qlen in [128, 256, 1024, 2048, 4096]:
316319
for klen in [128, 256, 1024, 2048]:
317-
inputs = torch.normal(0, 2, (batch, attn, qlen, klen), dtype=torch.float16, device='cuda:0')
318-
backward = torch.rand_like(inputs, dtype=torch.float16, device='cuda:0')
319-
masks = torch.randint(0, 2, (batch, 1, qlen, klen), dtype=torch.bool, device='cuda:0')
320+
inputs = torch.normal(0, 2, (batch, attn, qlen, klen), dtype=torch.float16, device=cur_platform.device_name(0))
321+
backward = torch.rand_like(inputs, dtype=torch.float16, device=cur_platform.device_name(0))
322+
masks = torch.randint(0, 2, (batch, 1, qlen, klen), dtype=torch.bool, device=cur_platform.device_name(0))
320323
softmax_results = scaled_masked_softmax_cuda.forward(inputs, masks, scale_t[0].item())
321324
back_grad = scaled_masked_softmax_cuda.backward(backward, softmax_results, scale_t[0].item())
322325

@@ -335,8 +338,8 @@ def test_allmasked_softmax_forward():
335338
scale_t = torch.tensor([1.0])
336339
for qlen in [128, 256, 1024, 2048, 4096]:
337340
for klen in [128, 256, 1024, 2048]:
338-
inputs = torch.normal(0, 2, (batch, attn, qlen, klen), dtype=torch.float16, device='cuda:0')
339-
masks = torch.ones((batch, 1, qlen, klen), dtype=torch.bool, device='cuda:0')
341+
inputs = torch.normal(0, 2, (batch, attn, qlen, klen), dtype=torch.float16, device=cur_platform.device_name(0))
342+
masks = torch.ones((batch, 1, qlen, klen), dtype=torch.bool, device=cur_platform.device_name(0))
340343
softmax_results = scaled_masked_softmax_cuda.forward(inputs, masks, scale_t[0].item())
341344
softmax_results_torch = torch.zeros_like(inputs)
342345
error = (softmax_results_torch - softmax_results).abs().max()
@@ -351,9 +354,9 @@ def test_allmasked_softmax_backward():
351354
scale_t = torch.tensor([1.0])
352355
for qlen in [128, 256, 1024, 2048, 4096]:
353356
for klen in [128, 256, 1024, 2048]:
354-
inputs = torch.normal(0, 2, (batch, attn, qlen, klen), dtype=torch.float16, device='cuda:0')
355-
backward = torch.rand_like(inputs, dtype=torch.float16, device='cuda:0')
356-
masks = torch.ones((batch, 1, qlen, klen), dtype=torch.bool, device='cuda:0')
357+
inputs = torch.normal(0, 2, (batch, attn, qlen, klen), dtype=torch.float16, device=cur_platform.device_name(0))
358+
backward = torch.rand_like(inputs, dtype=torch.float16, device=cur_platform.device_name(0))
359+
masks = torch.ones((batch, 1, qlen, klen), dtype=torch.bool, device=cur_platform.device_name(0))
357360
softmax_results = scaled_masked_softmax_cuda.forward(inputs, masks, scale_t[0].item())
358361
back_grad = scaled_masked_softmax_cuda.backward(backward, softmax_results, scale_t[0].item())
359362
inputs.requires_grad = True

flagscale/train/megatron/legacy/model/bert_model.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
from megatron.legacy.model.utils import scaled_init_method_normal
1717
from .module import MegatronModule
1818

19+
from megatron.plugin.platform import get_platform
20+
cur_platform = get_platform()
1921

2022
def bert_extended_attention_mask(attention_mask):
2123
# We create a 3D attention mask from a 2D tensor mask.
@@ -200,7 +202,7 @@ def forward(self, bert_model_input, attention_mask,
200202
output = torch.zeros(
201203
size=(embeddings.shape[0], embeddings.shape[2]),
202204
dtype=torch.float32,
203-
device=torch.cuda.current_device())
205+
device=cur_platform.current_device())
204206
for i, (embedding, mask) in enumerate(zip(embeddings, masks)):
205207
output[i, :] = torch.mean(embedding[1: mask - 1], dim=0)
206208

flagscale/train/megatron/legacy/model/module.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,12 @@
1010
from megatron.core import mpu, tensor_parallel
1111

1212

13-
_FLOAT_TYPES = (torch.FloatTensor, torch.cuda.FloatTensor)
14-
_HALF_TYPES = (torch.HalfTensor, torch.cuda.HalfTensor)
15-
_BF16_TYPES = (torch.BFloat16Tensor, torch.cuda.BFloat16Tensor)
13+
from megatron.plugin.platform import get_platform
14+
cur_platform = get_platform()
15+
16+
_FLOAT_TYPES = (torch.FloatTensor, cur_platform.FloatTensor)
17+
_HALF_TYPES = (torch.HalfTensor, cur_platform.HalfTensor)
18+
_BF16_TYPES = (torch.BFloat16Tensor, cur_platform.BFloat16Tensor)
1619

1720

1821
def param_is_not_shared(param):
@@ -117,7 +120,7 @@ def initialize_word_embeddings(self):
117120
# values.
118121
if mpu.is_rank_in_embedding_group(ignore_virtual=False):
119122
self.shared_embedding_or_output_weight().data = (
120-
self.shared_embedding_or_output_weight().data.cuda()
123+
self.shared_embedding_or_output_weight().data.to(cur_platform.device())
121124
)
122125
torch.distributed.all_reduce(
123126
self.shared_embedding_or_output_weight().data, group=mpu.get_embedding_group()

0 commit comments

Comments
 (0)