perf(config): route the config and manifest loaders through fast_safe_load
utils.fast_safe_load already exists, is pinned by tests/test_fast_safe_load.py, and its comment names exactly these payers: 'startup parses config.yaml and every plugin manifest, so the slow path cost ~0.9 s of cold start'. The migration was started — hermes_cli/config.py uses it eight times, hermes_cli/main.py and hermes_cli/plugins.py too — but the file-level loaders it was written for were never converted. The cost is config SIZE, and the size is the installer's doing: it seeds config.yaml by copying cli-config.yaml.example, 120,897 bytes of mostly comments. Nothing caches load_gateway_config() and it has 238 production call sites. Profiled before assuming a cause — reader.forward 34 ms, scanner.scan_to_next_token 28 ms, reader.peek 13 ms: the pure-Python PyYAML scanner, nothing else. Measured A/B on one realistic pass (gateway config + every bundled plugin description), medians of five runs, __pycache__ cleared between arms: 48.8 ms -> 2.46 ms. Per path: load_gateway_config 48.3 -> 1.84 ms, managed config.yaml 44.4 -> 1.03 ms, 105 plugin.yaml manifests 59.3 -> 5.9 ms. Same parse, same restricted tag set, same result — only the loader changes. Drops the three 'import yaml' statements the swap orphaned. (cherry picked from commit a4efd6a4506043159310eba02c32b673c455b88b)
This commit is contained in:
committed by
kshitij
parent
8646ddc19a
commit
16db511ed5
@@ -385,13 +385,15 @@ def read_yaml_layers(home: Path) -> dict:
|
||||
(``gateway.relay.relay_explicitly_disabled``) reads through here so it cannot disagree with
|
||||
``load_gateway_config()`` on which files count.
|
||||
"""
|
||||
import yaml
|
||||
|
||||
config_yaml_path = home / "config.yaml"
|
||||
yaml_cfg: dict = {}
|
||||
if config_yaml_path.exists():
|
||||
# The installer seeds config.yaml by copying cli-config.yaml.example (~120 KB, almost all
|
||||
# comments), and nothing caches this loader — so the pure-Python parser dominates the load.
|
||||
from utils import fast_safe_load
|
||||
|
||||
with open(config_yaml_path, encoding="utf-8") as f:
|
||||
yaml_cfg = yaml.safe_load(f) or {}
|
||||
yaml_cfg = fast_safe_load(f) or {}
|
||||
|
||||
from hermes_cli.config import _expand_env_vars
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ import threading
|
||||
from pathlib import Path
|
||||
from typing import Dict, Optional
|
||||
|
||||
import yaml
|
||||
|
||||
# Stale-module bridge: this module binds ``utils.file_signature`` at import time, so a fresh
|
||||
# import in a post-pull updater process (pre-handoff purge keeps root modules cached) dies
|
||||
@@ -107,7 +106,9 @@ def _load_managed_file(name: str, cache: Dict[str, tuple], parse) -> dict:
|
||||
|
||||
def load_managed_config() -> dict:
|
||||
"""Parsed managed config.yaml, or {} when absent/malformed (fail-open)."""
|
||||
return _load_managed_file("config.yaml", _CONFIG_CACHE, lambda p: yaml.safe_load(p.read_text(encoding="utf-8")) or {})
|
||||
from utils import fast_safe_load
|
||||
|
||||
return _load_managed_file("config.yaml", _CONFIG_CACHE, lambda p: fast_safe_load(p.read_text(encoding="utf-8")) or {})
|
||||
|
||||
|
||||
def load_managed_env() -> Dict[str, str]:
|
||||
|
||||
@@ -280,9 +280,10 @@ def _has_portable_manifest(plugin_dir: Path) -> bool:
|
||||
|
||||
def _load_yaml_manifest(manifest_file: Path):
|
||||
"""``yaml.safe_load`` of *manifest_file* (``{}`` when empty); raises on any read/parse error."""
|
||||
import yaml
|
||||
from utils import fast_safe_load
|
||||
|
||||
with open(manifest_file, encoding="utf-8") as f:
|
||||
return yaml.safe_load(f) or {}
|
||||
return fast_safe_load(f) or {}
|
||||
|
||||
|
||||
def _read_manifest(plugin_dir: Path) -> dict:
|
||||
|
||||
@@ -56,9 +56,10 @@ def iter_plugin_dirs(root: Path) -> List[Path]:
|
||||
def read_plugin_description(plugin_dir: Path) -> str:
|
||||
"""Return ``description`` from ``plugin.yaml`` (empty string if absent/unreadable)."""
|
||||
try:
|
||||
import yaml
|
||||
from utils import fast_safe_load
|
||||
|
||||
with open(plugin_dir / "plugin.yaml", encoding="utf-8-sig") as f:
|
||||
meta = yaml.safe_load(f) or {}
|
||||
meta = fast_safe_load(f) or {}
|
||||
return meta.get("description", "")
|
||||
except Exception:
|
||||
return ""
|
||||
|
||||
@@ -288,9 +288,9 @@ def _declares_model_provider_kind(plugin_dir: Path) -> bool:
|
||||
except Exception:
|
||||
return False
|
||||
try:
|
||||
import yaml
|
||||
from utils import fast_safe_load
|
||||
|
||||
data = yaml.safe_load(text)
|
||||
data = fast_safe_load(text)
|
||||
if isinstance(data, dict):
|
||||
return str(data.get("kind", "")).strip() == "model-provider"
|
||||
except Exception:
|
||||
|
||||
@@ -18,7 +18,7 @@ def _load_with_yaml_dict(yaml_dict: dict):
|
||||
patch("builtins.open", create=True) as mock_file:
|
||||
mock_file.return_value.__enter__ = lambda s: s
|
||||
mock_file.return_value.__exit__ = MagicMock(return_value=False)
|
||||
with patch("yaml.safe_load", return_value=yaml_dict):
|
||||
with patch("utils.fast_safe_load", return_value=yaml_dict):
|
||||
return load_gateway_config()
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user