# Copyright (c) ModelScope Contributors. All rights reserved. import torch.distributed as dist from tqdm import tqdm from swift.utils import to_device class DataLoaderDispatcher: def __init__(self, base_dataloader, device=None, skip_batches: int = 0): self.base_dataloader = base_dataloader self.device = device self.skip_batches = skip_batches @property def rank(self): return dist.get_rank(self.group) if dist.is_initialized() else 0 @property def world_size(self): return dist.get_world_size(self.group) if dist.is_initialized() else 1 @property def group(self): return dist.group.WORLD if dist.is_initialized() else 1 def _scatter_object_list(self, inputs): if not dist.is_initialized(): return inputs[0] outputs = [None] global_src_rank = dist.get_global_rank(self.group, 0) dist.scatter_object_list(outputs, inputs, global_src_rank, group=self.group) return outputs[0] def _skip_batches(self, base_iter): if self.rank == 0 and self.skip_batches > 0: for _ in tqdm(range(self.skip_batches), dynamic_ncols=True, desc='Skip Batches: '): [next(base_iter) for _ in range(self.world_size)] def __iter__(self): base_iter = iter(self.base_dataloader) self._skip_batches(base_iter) while True: if self.rank == 0: try: data = [next(base_iter) for _ in range(self.world_size)] except StopIteration: data = [None] * self.world_size data = self._scatter_object_list(data) else: data = self._scatter_object_list(None) if data is None: break if self.device: data = to_device(data, self.device) yield data