Skip to content

Commit 2ab4c09

Browse files
committed
fix: Use default torch timeout for nccl watchdog unless overridden
The default value is recommended, and we should not change it in production. The knob may still be useful for debugging or testing purposes though. Signed-off-by: Ihar Hrachyshka <ihar.hrachyshka@gmail.com>
1 parent fd03460 commit 2ab4c09

2 files changed

Lines changed: 53 additions & 6 deletions

File tree

src/instructlab/training/main_ds.py

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
# Standard
44
from copy import deepcopy
5+
import functools
56
from pathlib import Path
67
import argparse
78
import datetime
@@ -533,6 +534,22 @@ def train(
533534
)
534535

535536

537+
def _get_collective_timeout() -> datetime.timedelta | None:
538+
timeout_var = os.getenv("INSTRUCTLAB_NCCL_TIMEOUT_MS")
539+
if timeout_var is None:
540+
return None
541+
542+
try:
543+
timeout = int(timeout_var)
544+
except ValueError:
545+
timeout = -1
546+
547+
if timeout <= 0:
548+
raise ValueError(f"Invalid value for INSTRUCTLAB_NCCL_TIMEOUT_MS: {timeout_var}. Must be a positive integer.")
549+
550+
return datetime.timedelta(milliseconds=timeout)
551+
552+
536553
def main(args):
537554
# Third Party
538555
import yaml
@@ -566,15 +583,17 @@ def main(args):
566583
model_conf = AutoConfig.from_pretrained(args.model_name_or_path)
567584
args.model_type = model_conf.model_type
568585

569-
# solution discovered from torchtune https://github.com/pytorch/torchtune/issues/2093
570-
# gets converted to a timedelta of 1:40:00 if the default is kept
571-
nccl_timeout = int(os.getenv("INSTRUCTLAB_NCCL_TIMEOUT_MS", "6000000"))
572586
#### distributed init #####
573587
torch.cuda.set_device(int(os.environ["LOCAL_RANK"]))
574588
args.local_rank = int(os.environ["LOCAL_RANK"])
575-
torch.distributed.init_process_group(
576-
"nccl", timeout=datetime.timedelta(milliseconds=nccl_timeout)
577-
)
589+
590+
timeout = _get_collective_timeout()
591+
init = functools.partial(torch.distributed.init_process_group, "nccl")
592+
if timeout is not None:
593+
init(timeout=timeout)
594+
else:
595+
init()
596+
578597
args.global_rank = torch.distributed.get_rank()
579598
tensor = torch.ByteTensor([False]).cuda()
580599
torch.distributed.all_reduce(tensor)

tests/unit/test_main_ds.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import datetime
2+
from unittest import mock
3+
4+
import pytest
5+
6+
from instructlab.training import main_ds
7+
8+
9+
def test__get_collective_timeout():
10+
# Test with default timeout
11+
assert main_ds._get_collective_timeout() is None
12+
13+
# Test with custom timeout
14+
timeout = 1234
15+
with mock.patch.dict(main_ds.os.environ, {"INSTRUCTLAB_NCCL_TIMEOUT_MS": str(timeout)}):
16+
assert main_ds._get_collective_timeout() == datetime.timedelta(milliseconds=timeout)
17+
18+
# Test with invalid timeout (negative)
19+
invalid_timeout = "-100"
20+
with mock.patch.dict(main_ds.os.environ, {"INSTRUCTLAB_NCCL_TIMEOUT_MS": invalid_timeout}):
21+
with pytest.raises(ValueError):
22+
main_ds._get_collective_timeout()
23+
24+
# Test with invalid timeout (string)
25+
invalid_timeout = "invalid"
26+
with mock.patch.dict(main_ds.os.environ, {"INSTRUCTLAB_NCCL_TIMEOUT_MS": invalid_timeout}):
27+
with pytest.raises(ValueError):
28+
main_ds._get_collective_timeout()

0 commit comments

Comments
 (0)