Skip to content

Commit 461a3b4

Browse files
authored
Merge branch 'main' into config-loss-fn
2 parents a87277e + 4360aa2 commit 461a3b4

3 files changed

Lines changed: 73 additions & 16 deletions

File tree

scripts/data_generation_offline.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
DEFAULT_MAX_RETRIES,
3131
DEFAULT_REQUEST_TIMEOUT,
3232
generate_hidden_states_async,
33+
wait_for_lock_async,
3334
)
3435
from speculators.train.data import build_client_item
3536
from speculators.train.logger import setup_root_logger
@@ -170,21 +171,21 @@ def parse_args():
170171
def get_existing_hidden_state_indices(output_path: Path) -> list[int]:
171172
"""Find existing `hs_i.safetensors` files (where i is the file index)"""
172173

173-
existing_file_indices = []
174+
existing_file_indices_set: set[int] = set()
174175

175176
if not output_path.exists():
176-
return existing_file_indices
177+
return []
177178

178179
for file_path in output_path.iterdir():
179180
if file_path.name.startswith("hs_") and file_path.name.endswith(".safetensors"):
180181
index_str = file_path.stem[3:] # Remove "hs_" prefix
181182
try:
182183
file_index = int(index_str)
183-
existing_file_indices.append(file_index)
184+
existing_file_indices_set.add(file_index)
184185
except ValueError:
185186
continue
186187

187-
return sorted(existing_file_indices)
188+
return sorted(existing_file_indices_set)
188189

189190

190191
def get_indices_to_process(
@@ -247,7 +248,7 @@ def check_safetensors_file(path: Path, tokens: list[int]):
247248
)
248249

249250

250-
async def worker(
251+
async def worker( # noqa: C901
251252
client,
252253
model: str,
253254
queue: "asyncio.Queue[dict[str, Any]]",
@@ -288,6 +289,10 @@ async def worker(
288289
timeout=request_timeout,
289290
max_retries=max_retries,
290291
)
292+
lock_path = hidden_states_path + ".lock"
293+
if Path(lock_path).exists(): # noqa: ASYNC240
294+
await wait_for_lock_async(lock_path)
295+
291296
async with write_semaphore: # Limit number of active disk writes
292297
await asyncio.to_thread(
293298
shutil.move, hidden_states_path, target_hidden_states_path

src/speculators/data_generation/vllm_client.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import asyncio
2+
import fcntl
23
import functools
34
import logging
5+
import os
46
import time
57
from typing import TYPE_CHECKING, Any, TypedDict
68

@@ -122,6 +124,47 @@ class ClientItem(TypedDict):
122124
instead of passing `token_ids` to Completions API."""
123125

124126

127+
async def _poll_lock_async(fd, poll_interval):
128+
while True:
129+
try:
130+
fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
131+
return
132+
except BlockingIOError:
133+
await asyncio.sleep(poll_interval)
134+
135+
136+
async def wait_for_lock_async(lock_path, timeout=10.0, poll_interval=0.1):
137+
fd = os.open(lock_path, os.O_RDONLY)
138+
try:
139+
await asyncio.wait_for(_poll_lock_async(fd, poll_interval), timeout=timeout)
140+
except BaseException:
141+
os.close(fd)
142+
raise
143+
os.close(fd)
144+
os.remove(lock_path)
145+
146+
147+
def wait_for_lock(lock_path, timeout=10.0, poll_interval=0.1):
148+
fd = os.open(lock_path, os.O_RDONLY)
149+
try:
150+
deadline = time.monotonic() + timeout
151+
while True:
152+
try:
153+
fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
154+
break
155+
except BlockingIOError:
156+
if time.monotonic() >= deadline:
157+
raise TimeoutError(
158+
f"Timed out waiting for lock: {lock_path}"
159+
) from None
160+
time.sleep(poll_interval)
161+
except BaseException:
162+
os.close(fd)
163+
raise
164+
os.close(fd)
165+
os.remove(lock_path)
166+
167+
125168
@with_retries
126169
async def generate_hidden_states_async(
127170
client: openai.AsyncClient,

src/speculators/train/data.py

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
DEFAULT_REQUEST_TIMEOUT,
2222
ClientItem,
2323
generate_hidden_states,
24+
wait_for_lock,
2425
)
2526
from speculators.train.noise_transforms import TransformTensors
2627

@@ -259,6 +260,11 @@ def _compute_approx_lengths(self) -> list[int]:
259260
def _maybe_load_hs_file(self, index: int) -> dict[str, torch.Tensor] | None:
260261
file_idx = self._map_to_file_idx(index)
261262
candidate_path = self.hidden_states_path / f"hs_{file_idx}.safetensors"
263+
264+
lock_path = str(candidate_path) + ".lock"
265+
if Path(lock_path).exists():
266+
wait_for_lock(lock_path)
267+
262268
if candidate_path.exists():
263269
return load_file(candidate_path)
264270

@@ -279,19 +285,22 @@ def _maybe_generate_hs(self, index: int) -> dict[str, torch.Tensor] | None:
279285
timeout=self.request_timeout,
280286
max_retries=self.max_retries,
281287
)
282-
except Exception as e: # noqa: BLE001
283-
warnings.warn(str(e), stacklevel=1)
284-
return None
285288

286-
loaded_hs = load_file(hs_filepath)
289+
loaded_hs = load_file(hs_filepath)
287290

288-
match self.on_generate:
289-
case "cache":
290-
file_idx = self._map_to_file_idx(index)
291-
target_path = self.hidden_states_path / f"hs_{file_idx}.safetensors"
292-
shutil.move(hs_filepath, target_path)
293-
case "delete":
294-
Path(hs_filepath).unlink()
291+
match self.on_generate:
292+
case "cache":
293+
file_idx = self._map_to_file_idx(index)
294+
target_path = self.hidden_states_path / f"hs_{file_idx}.safetensors"
295+
shutil.move(hs_filepath, target_path)
296+
case "delete":
297+
Path(hs_filepath).unlink()
298+
except Exception as e: # noqa: BLE001
299+
warnings.warn(
300+
f"Failed to load/cache hidden states for sample {index}: {e}",
301+
stacklevel=1,
302+
)
303+
return None
295304

296305
return loaded_hs
297306

0 commit comments

Comments
 (0)