From 2eb93c00283898ba747f8b801508228a264a5c2e Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Thu, 27 Aug 2026 17:17:02 +0000 Subject: [PATCH] =?UTF-8?q?=E2=9A=A1=20Bolt:=20[performance=20improvement]?= =?UTF-8?q?=20Memoize=20deterministic=20regex=20compilations?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added `@functools.lru_cache` to `_compiled_context_lexicon` in `openmed/openmed/clinical/context.py` and section aliases in `openmed/openmed/clinical/lexicons/section_headers.py`. Modified dictionary return types to `types.MappingProxyType` to prevent downstream mutation of cached instances. Recorded performance learning in `.jules/bolt.md`. Co-authored-by: zrt219 <199104500+zrt219@users.noreply.github.com> --- .jules/bolt.md | 3 +++ openmed/openmed/clinical/context.py | 2 ++ .../openmed/clinical/lexicons/section_headers.py | 14 +++++++++----- 3 files changed, 14 insertions(+), 5 deletions(-) create mode 100644 .jules/bolt.md diff --git a/.jules/bolt.md b/.jules/bolt.md new file mode 100644 index 0000000..fd147f6 --- /dev/null +++ b/.jules/bolt.md @@ -0,0 +1,3 @@ +## 2024-08-27 - Memoize deterministic regex compilations in NLP pipelines +**Learning:** In openmed text processing pipelines, compiling deterministic regex patterns or lexicons dynamically inside functions called during string/span evaluations (e.g. `_compiled_context_lexicon` in `openmed.clinical.context`) creates a severe performance bottleneck. +**Action:** Always memoize these functions using `@functools.lru_cache`, ensuring that the function signature contains explicitly typed, hashable arguments and returns an immutable object (e.g., `MappingProxyType`, `tuple`, or frozen dataclass) to prevent downstream callers from modifying the cached state. diff --git a/openmed/openmed/clinical/context.py b/openmed/openmed/clinical/context.py index 9fd11df..439abc2 100644 --- a/openmed/openmed/clinical/context.py +++ b/openmed/openmed/clinical/context.py @@ -35,6 +35,7 @@ from __future__ import annotations +import functools import re from collections.abc import Iterable, Iterator, Mapping, Sequence from dataclasses import dataclass, replace @@ -154,6 +155,7 @@ class _CompiledContextLexicon: backward_context_cues: frozenset[str] +@functools.lru_cache(maxsize=16) def _compiled_context_lexicon(language: str | None = None) -> _CompiledContextLexicon: lexicon = get_clinical_cue_lexicon(language) token_boundaries = lexicon.token_boundaries diff --git a/openmed/openmed/clinical/lexicons/section_headers.py b/openmed/openmed/clinical/lexicons/section_headers.py index 81c2267..015b214 100644 --- a/openmed/openmed/clinical/lexicons/section_headers.py +++ b/openmed/openmed/clinical/lexicons/section_headers.py @@ -7,6 +7,8 @@ from __future__ import annotations +import functools +import types import unicodedata from collections.abc import Mapping from dataclasses import dataclass @@ -236,7 +238,8 @@ def available_section_languages() -> tuple[str, ...]: return tuple(sorted(_LEXICONS)) -def section_header_aliases(language: str | None = None) -> dict[str, str]: +@functools.lru_cache(maxsize=16) +def section_header_aliases(language: str | None = None) -> Mapping[str, str]: """Return raw section header aliases mapped to canonical section keys.""" languages = ( @@ -249,16 +252,17 @@ def section_header_aliases(language: str | None = None) -> dict[str, str]: aliases[canonical] = canonical for header in headers: aliases[header] = canonical - return aliases + return types.MappingProxyType(aliases) -def normalized_section_header_aliases(language: str | None = None) -> dict[str, str]: +@functools.lru_cache(maxsize=16) +def normalized_section_header_aliases(language: str | None = None) -> Mapping[str, str]: """Return normalized section header aliases mapped to canonical keys.""" - return { + return types.MappingProxyType({ normalize_section_header(header): canonical for header, canonical in section_header_aliases(language).items() - } + }) def section_lexicon_stats(