Files
wehub-resource-sync a203934033
Lint test / lint (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:34:58 +08:00

133 lines
4.7 KiB
Python

# Copyright (c) ModelScope Contributors. All rights reserved.
import bisect
import mmap
import os
import pickle
import threading
from modelscope.hub.utils.utils import get_cache_dir
from queue import Queue
from torch.utils.data import Dataset
from typing import Any, List
class IndexedDatasetBuilder:
CHUNK_SIZE = 1e10
def __init__(self, dataset_name: str):
self.cache_dir = IndexedDataset.get_cache_dir(dataset_name)
self.n_shard = 1
self.bin_path = os.path.join(self.cache_dir, IndexedDataset.BIN_FNAME.format(0))
self.idx_path = os.path.join(self.cache_dir, IndexedDataset.IDX_FNAME)
if os.path.exists(self.bin_path):
os.remove(self.bin_path)
self.bin_file = open(self.bin_path, 'ab')
self.length_list = []
self.idx_list = [0]
self.shard_offset = [0]
self._thread = None
self._queue = Queue(maxsize=1000)
def _write_worker(self):
while True:
items = self._queue.get()
if items is None:
break
bin_buffer = []
for item in items:
item_buffer = pickle.dumps(item)
bin_buffer.append(item_buffer)
self.idx_list.append(self.idx_list[-1] + len(item_buffer))
self.length_list.append(item['length'])
self.bin_file.write(b''.join(bin_buffer))
offset = self.idx_list[-1] - self.shard_offset[-1]
if offset >= self.CHUNK_SIZE:
self.bin_file.close()
self.bin_path = os.path.join(self.cache_dir, IndexedDataset.BIN_FNAME.format(self.n_shard))
self.shard_offset.append(self.shard_offset[-1] + offset)
self.n_shard += 1
if os.path.exists(self.bin_path):
os.remove(self.bin_path)
self.bin_file = open(self.bin_path, 'ab')
def add_items(self, items: List[Any]) -> None:
if self._thread is None:
self._thread = threading.Thread(target=self._write_worker, daemon=True)
self._thread.start()
self._queue.put(items)
def finalize(self):
if self._thread is not None:
self._queue.put(None)
self._thread.join()
self.bin_file.close()
idx_obj = {
'idx': self.idx_list,
'length': self.length_list,
'n_shard': self.n_shard,
'shard_offset': self.shard_offset,
}
with open(self.idx_path, 'wb') as f:
pickle.dump(idx_obj, f)
class BinReader:
def __init__(self, bin_path: str):
self.bin_path = bin_path
self.file = open(bin_path, 'rb')
try:
self.mm = mmap.mmap(self.file.fileno(), 0, access=mmap.ACCESS_READ)
except ValueError:
# For example, self.file is an empty file.
self.mm = None
def read_buffer(self, offset: int, size: int) -> bytes:
if offset < 0 or size < 0 or offset + size > len(self.mm):
raise ValueError('Invalid offset or size')
return self.mm[offset:offset + size]
def __del__(self):
if self.mm is not None:
self.mm.close()
self.file.close()
class IndexedDataset(Dataset):
BIN_FNAME = 'data-{:05d}.bin'
IDX_FNAME = 'data.idx'
@staticmethod
def get_cache_dir(dataset_name: str):
cache_dir = os.getenv('PACKING_CACHE') or os.path.join(get_cache_dir(), 'tmp')
cache_dir = os.path.join(cache_dir, dataset_name)
os.makedirs(cache_dir, exist_ok=True)
assert dataset_name is not None, f'dataset_name: {dataset_name}'
return cache_dir
def __init__(self, dataset_name: str):
self.dataset_name = dataset_name
cache_dir = self.get_cache_dir(dataset_name)
self.idx_path = os.path.join(cache_dir, self.IDX_FNAME)
with open(self.idx_path, 'rb') as f:
idx_obj = pickle.load(f)
self.idx_list = idx_obj['idx']
self.length_list = idx_obj['length']
self.n_shard = idx_obj['n_shard']
self.shard_offset = idx_obj['shard_offset']
self.bin_readers = []
for i in range(self.n_shard):
bin_path = os.path.join(cache_dir, self.BIN_FNAME.format(i))
self.bin_readers.append(BinReader(bin_path))
def __getitem__(self, index: int):
if index < 0:
index = index % len(self)
idx, idx_next = self.idx_list[index], self.idx_list[index + 1]
num_shard = bisect.bisect_right(self.shard_offset, idx)
offset = self.shard_offset[num_shard - 1]
buffer = self.bin_readers[num_shard - 1].read_buffer(idx - offset, idx_next - idx)
return pickle.loads(buffer)
def __len__(self):
return len(self.idx_list) - 1