Repository: Project-MONAI/MONAI
File: monai/data/grid_dataset.py (Python)

Code before:
            if isinstance(data, Iterator):
                raise TypeError("Data can not be iterator when cache is True")
            self.set_data(data)  # type: ignore

    def set_data(self, data: Sequence) -> None:
        """
        Set the input data and run deterministic transforms to generate cache content.

        Note: should call this func after an entire epoch and must set `persistent_workers=False`
        in PyTorch DataLoader, because it needs to create new worker processes based on new
        generated cache content.

        """
        self.data = data

        def _compute_cache_num(data_len: int):
            self.cache_num = min(int(self.set_num), int(data_len * self.set_rate), data_len)

        # only compute cache for the unique items of dataset, and record the last index for duplicated items
        mapping = {self.hash_func(v): i for i, v in enumerate(self.data)}
        _compute_cache_num(len(mapping))
        self._hash_keys = list(mapping)[: self.cache_num]
        indices = list(mapping.values())[: self.cache_num]

        self._cache = self._fill_cache(indices)
        return

    def _fill_cache(self, indices=None) -> list:
        """
        Compute and fill the cache content from data source.

        Args:
            indices: target indices in the `self.data` source to compute cache.
                if None, use the first `cache_num` items.

        """
        if self.cache_num <= 0:
            return []
        if indices is None:
            indices = list(range(self.cache_num))
        if self.progress and not has_tqdm:
            warnings.warn("tqdm is not installed, will not show the caching progress bar.")
        with ThreadPool(self.num_workers) as p:
            if self.progress and has_tqdm:
                return list(tqdm(p.imap(self._load_cache_item, indices), total=len(indices), desc="Loading dataset"))
            return list(p.imap(self._load_cache_item, indices))

    def _load_cache_item(self, idx: int):
        """
        Args:
            idx: the index of the input data sequence.

Review comment (performance):
```suggestion
        # only compute cache for the unique items of dataset, and record the last index for duplicated items
        mapping = {self.hash_func(v): i for i, v in enumerate(self.data)}
        self.cache_num = min(int(self.set_num), int(len(mapping)* self.set_rate), len(mapping))
        self._hash_keys = list(mapping)[: self.cache_num]
        indices = list(mapping.values())[: self.cache_num]
        self._cache = self._fill_cache(indices)
```

Code after:
        if self.cache:
            if isinstance(data, Iterator):
                raise TypeError("Data can not be iterator when cache is True")
            self.set_data(data)  # type: ignore

    def set_data(self, data: Sequence) -> None:
        """
        Set the input data and run deterministic transforms to generate cache content.

        Note: should call this func after an entire epoch and must set `persistent_workers=False`
        in PyTorch DataLoader, because it needs to create new worker processes based on new
        generated cache content.

        """
        self.data = data

        # only compute cache for the unique items of dataset, and record the last index for duplicated items
        mapping = {self.hash_func(v): i for i, v in enumerate(self.data)}
        self.cache_num = min(int(self.set_num), int(len(mapping) * self.set_rate), len(mapping))
        self._hash_keys = list(mapping)[: self.cache_num]
        indices = list(mapping.values())[: self.cache_num]
        self._cache, self._cache_other = zip(*self._fill_cache(indices))  # type: ignore

    def _fill_cache(self, indices=None) -> list:
        """
        Compute and fill the cache content from data source.

        Args:
            indices: target indices in the `self.data` source to compute cache.
                if None, use the first `cache_num` items.

        """
        if self.cache_num <= 0:
            return []
        if indices is None:
            indices = list(range(self.cache_num))
        if self.progress and not has_tqdm:
            warnings.warn("tqdm is not installed, will not show the caching progress bar.")

        pfunc = tqdm if self.progress and has_tqdm else (lambda v, **_: v)
        with ThreadPool(self.num_workers) as p:
            return list(pfunc(p.imap(self._load_cache_item, indices), total=len(indices), desc="Loading dataset"))

    def _load_cache_item(self, idx: int):
        """
        Args:
            idx: the index of the input data sequence.
        """
        item = self.data[idx]  # type: ignore
        patch_cache, other_cache = [], []
        for patch, *others in self.patch_iter(item):