mirror of
https://github.com/huggingface/lerobot.git
synced 2026-07-24 02:06:15 +00:00
Instrument HfFileSystem range requests
This commit is contained in:
@@ -661,6 +661,63 @@ def _print_range_timing_summary(fetch_pool: dict[str, float]) -> None:
|
|||||||
if status_counts:
|
if status_counts:
|
||||||
summary = ", ".join(f"{status}={count:.0f}" for status, count in sorted(status_counts.items()))
|
summary = ", ".join(f"{status}={count:.0f}" for status, count in sorted(status_counts.items()))
|
||||||
print(f"| http status counts | {summary} |")
|
print(f"| http status counts | {summary} |")
|
||||||
|
for method in ("head", "get"):
|
||||||
|
request_count = fetch_pool.get(f"range_hffs_{method}_requests", 0.0)
|
||||||
|
if request_count <= 0:
|
||||||
|
continue
|
||||||
|
print(f"| hffs {method.upper()} requests/range | {request_count / range_jobs:.3f} |")
|
||||||
|
print(
|
||||||
|
f"| hffs {method.upper()} total | {fetch_pool[f'range_hffs_{method}_s'] * 1000 / range_jobs:.3f} |"
|
||||||
|
)
|
||||||
|
retries = fetch_pool.get(f"range_hffs_{method}_retries", 0.0)
|
||||||
|
exceptions = fetch_pool.get(f"range_hffs_{method}_exception_attempts", 0.0)
|
||||||
|
if retries:
|
||||||
|
print(f"| hffs {method.upper()} retries/range | {retries / range_jobs:.3f} |")
|
||||||
|
print(
|
||||||
|
f"| hffs {method.upper()} retry sleep | "
|
||||||
|
f"{fetch_pool.get(f'range_hffs_{method}_retry_sleep_s', 0.0) * 1000 / range_jobs:.3f} |"
|
||||||
|
)
|
||||||
|
if exceptions:
|
||||||
|
print(f"| hffs {method.upper()} exceptions/range | {exceptions / range_jobs:.3f} |")
|
||||||
|
print(
|
||||||
|
f"| hffs {method.upper()} failed attempts | "
|
||||||
|
f"{fetch_pool.get(f'range_hffs_{method}_failed_attempt_s', 0.0) * 1000 / range_jobs:.3f} |"
|
||||||
|
)
|
||||||
|
bytes_read = fetch_pool.get(f"range_hffs_{method}_bytes", 0.0)
|
||||||
|
total_s = fetch_pool.get(f"range_hffs_{method}_s", 0.0)
|
||||||
|
if bytes_read > 0 and total_s > 0:
|
||||||
|
print(f"| hffs {method.upper()} MiB/s | {bytes_read / total_s / 1024**2:.1f} |")
|
||||||
|
hffs_status_counts = {
|
||||||
|
key.removeprefix(f"range_hffs_{method}_status_"): value
|
||||||
|
for key, value in fetch_pool.items()
|
||||||
|
if key.startswith(f"range_hffs_{method}_status_")
|
||||||
|
}
|
||||||
|
if hffs_status_counts:
|
||||||
|
summary = ", ".join(
|
||||||
|
f"{status}={count:.0f}" for status, count in sorted(hffs_status_counts.items())
|
||||||
|
)
|
||||||
|
print(f"| hffs {method.upper()} status counts | {summary} |")
|
||||||
|
hffs_failed_status_counts = {
|
||||||
|
key.removeprefix(f"range_hffs_{method}_failed_status_"): value
|
||||||
|
for key, value in fetch_pool.items()
|
||||||
|
if key.startswith(f"range_hffs_{method}_failed_status_")
|
||||||
|
}
|
||||||
|
if hffs_failed_status_counts:
|
||||||
|
summary = ", ".join(
|
||||||
|
f"{status}={count:.0f}" for status, count in sorted(hffs_failed_status_counts.items())
|
||||||
|
)
|
||||||
|
print(f"| hffs {method.upper()} failed status counts | {summary} |")
|
||||||
|
hffs_exception_counts = {
|
||||||
|
key.removeprefix(f"range_hffs_{method}_exception_"): value
|
||||||
|
for key, value in fetch_pool.items()
|
||||||
|
if key.startswith(f"range_hffs_{method}_exception_")
|
||||||
|
and key != f"range_hffs_{method}_exception_attempts"
|
||||||
|
}
|
||||||
|
if hffs_exception_counts:
|
||||||
|
summary = ", ".join(
|
||||||
|
f"{name}={count:.0f}" for name, count in sorted(hffs_exception_counts.items())
|
||||||
|
)
|
||||||
|
print(f"| hffs {method.upper()} exception counts | {summary} |")
|
||||||
chunks = fetch_pool.get("range_chunks", 0.0)
|
chunks = fetch_pool.get("range_chunks", 0.0)
|
||||||
if chunks > 0:
|
if chunks > 0:
|
||||||
bytes_read = fetch_pool.get("range_bytes", 0.0)
|
bytes_read = fetch_pool.get("range_bytes", 0.0)
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ from concurrent.futures import Future, ThreadPoolExecutor
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from importlib import metadata
|
from importlib import metadata
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from types import MethodType
|
||||||
from typing import Any
|
from typing import Any
|
||||||
from urllib.parse import quote, urljoin, urlparse
|
from urllib.parse import quote, urljoin, urlparse
|
||||||
|
|
||||||
@@ -25,7 +26,7 @@ import fsspec
|
|||||||
import httpx
|
import httpx
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from huggingface_hub import HfApi, HfFileSystem, constants
|
from huggingface_hub import HfApi, HfFileSystem, constants
|
||||||
from huggingface_hub.utils import hf_raise_for_status
|
from huggingface_hub.utils import get_session, hf_raise_for_status
|
||||||
|
|
||||||
from lerobot.datasets.dataset_metadata import LeRobotDatasetMetadata
|
from lerobot.datasets.dataset_metadata import LeRobotDatasetMetadata
|
||||||
from lerobot.datasets.mp4 import Mp4Index, Mp4SampleSlice, fetch_mp4_index, synthesize_mp4
|
from lerobot.datasets.mp4 import Mp4Index, Mp4SampleSlice, fetch_mp4_index, synthesize_mp4
|
||||||
@@ -85,6 +86,7 @@ class ThreadLocalRangeFetcher:
|
|||||||
handle = self.fs.open(
|
handle = self.fs.open(
|
||||||
self._url(relative_path), "rb", block_size=self.block_size, cache_type=self.cache_type
|
self._url(relative_path), "rb", block_size=self.block_size, cache_type=self.cache_type
|
||||||
)
|
)
|
||||||
|
self._instrument_hf_handle(handle)
|
||||||
handles[relative_path] = handle
|
handles[relative_path] = handle
|
||||||
return handle
|
return handle
|
||||||
|
|
||||||
@@ -115,6 +117,117 @@ class ThreadLocalRangeFetcher:
|
|||||||
for key, value in kwargs.items():
|
for key, value in kwargs.items():
|
||||||
self._timing_totals[key] = self._timing_totals.get(key, 0.0) + value
|
self._timing_totals[key] = self._timing_totals.get(key, 0.0) + value
|
||||||
|
|
||||||
|
def _instrument_hf_handle(self, handle: Any) -> None:
|
||||||
|
if getattr(handle, "_lerobot_range_timing", False):
|
||||||
|
return
|
||||||
|
if not hasattr(handle, "_request_with_retry"):
|
||||||
|
return
|
||||||
|
|
||||||
|
def request_with_retry(
|
||||||
|
handle_self,
|
||||||
|
method: str,
|
||||||
|
url: str,
|
||||||
|
*,
|
||||||
|
headers: dict[str, str],
|
||||||
|
follow_redirects: bool | None = None,
|
||||||
|
max_retries: int = 5,
|
||||||
|
) -> httpx.Response:
|
||||||
|
from huggingface_hub.hf_file_system import _RANGE_RETRY_EXCEPTIONS, _RANGE_RETRY_STATUS_CODES
|
||||||
|
|
||||||
|
method_key = method.lower()
|
||||||
|
sleep_time = 1.0
|
||||||
|
retry_attempts = 0.0
|
||||||
|
retry_sleep_s = 0.0
|
||||||
|
failed_attempt_s = 0.0
|
||||||
|
exception_attempts = 0.0
|
||||||
|
extra_counts: dict[str, float] = {}
|
||||||
|
call_start = time.perf_counter()
|
||||||
|
request_kwargs: dict[str, Any] = {
|
||||||
|
"headers": headers,
|
||||||
|
"timeout": constants.HF_HUB_DOWNLOAD_TIMEOUT,
|
||||||
|
}
|
||||||
|
if follow_redirects is not None:
|
||||||
|
request_kwargs["follow_redirects"] = follow_redirects
|
||||||
|
|
||||||
|
for attempt in range(max_retries + 1):
|
||||||
|
attempt_start = time.perf_counter()
|
||||||
|
try:
|
||||||
|
response = get_session().request(method, url, **request_kwargs)
|
||||||
|
except _RANGE_RETRY_EXCEPTIONS as exc:
|
||||||
|
failed_attempt_s += time.perf_counter() - attempt_start
|
||||||
|
exception_attempts += 1.0
|
||||||
|
key = f"range_hffs_{method_key}_exception_{type(exc).__name__}"
|
||||||
|
extra_counts[key] = extra_counts.get(key, 0.0) + 1.0
|
||||||
|
if attempt == max_retries:
|
||||||
|
self._record_hffs_request_timing(
|
||||||
|
method_key,
|
||||||
|
time.perf_counter() - call_start,
|
||||||
|
retry_attempts,
|
||||||
|
retry_sleep_s,
|
||||||
|
failed_attempt_s,
|
||||||
|
exception_attempts,
|
||||||
|
None,
|
||||||
|
0,
|
||||||
|
extra_counts,
|
||||||
|
)
|
||||||
|
raise
|
||||||
|
else:
|
||||||
|
elapsed = time.perf_counter() - attempt_start
|
||||||
|
if response.status_code not in _RANGE_RETRY_STATUS_CODES or attempt == max_retries:
|
||||||
|
self._record_hffs_request_timing(
|
||||||
|
method_key,
|
||||||
|
time.perf_counter() - call_start,
|
||||||
|
retry_attempts,
|
||||||
|
retry_sleep_s,
|
||||||
|
failed_attempt_s,
|
||||||
|
exception_attempts,
|
||||||
|
response.status_code,
|
||||||
|
len(response.content),
|
||||||
|
extra_counts,
|
||||||
|
)
|
||||||
|
return response
|
||||||
|
|
||||||
|
failed_attempt_s += elapsed
|
||||||
|
key = f"range_hffs_{method_key}_failed_status_{response.status_code}"
|
||||||
|
extra_counts[key] = extra_counts.get(key, 0.0) + 1.0
|
||||||
|
response.close()
|
||||||
|
|
||||||
|
time.sleep(sleep_time)
|
||||||
|
retry_attempts += 1.0
|
||||||
|
retry_sleep_s += sleep_time
|
||||||
|
sleep_time = min(8.0, sleep_time * 2)
|
||||||
|
|
||||||
|
raise RuntimeError("unreachable")
|
||||||
|
|
||||||
|
handle._request_with_retry = MethodType(request_with_retry, handle)
|
||||||
|
handle._lerobot_range_timing = True
|
||||||
|
|
||||||
|
def _record_hffs_request_timing(
|
||||||
|
self,
|
||||||
|
method_key: str,
|
||||||
|
total_s: float,
|
||||||
|
retry_attempts: float,
|
||||||
|
retry_sleep_s: float,
|
||||||
|
failed_attempt_s: float,
|
||||||
|
exception_attempts: float,
|
||||||
|
status_code: int | None,
|
||||||
|
byte_count: int,
|
||||||
|
extra_counts: dict[str, float],
|
||||||
|
) -> None:
|
||||||
|
timings = {
|
||||||
|
f"range_hffs_{method_key}_requests": 1.0,
|
||||||
|
f"range_hffs_{method_key}_s": total_s,
|
||||||
|
f"range_hffs_{method_key}_retries": retry_attempts,
|
||||||
|
f"range_hffs_{method_key}_retry_sleep_s": retry_sleep_s,
|
||||||
|
f"range_hffs_{method_key}_failed_attempt_s": failed_attempt_s,
|
||||||
|
f"range_hffs_{method_key}_exception_attempts": exception_attempts,
|
||||||
|
f"range_hffs_{method_key}_bytes": float(byte_count),
|
||||||
|
}
|
||||||
|
if status_code is not None:
|
||||||
|
timings[f"range_hffs_{method_key}_status_{status_code}"] = 1.0
|
||||||
|
timings.update(extra_counts)
|
||||||
|
self._record_timing(**timings)
|
||||||
|
|
||||||
def timing_summary(self) -> dict[str, float]:
|
def timing_summary(self) -> dict[str, float]:
|
||||||
with self._timing_lock:
|
with self._timing_lock:
|
||||||
return dict(self._timing_totals)
|
return dict(self._timing_totals)
|
||||||
|
|||||||
Reference in New Issue
Block a user