diff --git a/.jules/bolt.md b/.jules/bolt.md index 8ce7ce8f7..4c65605a4 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -77,3 +77,7 @@ ## 2024-11-20 - Optimize multiple tuple generation from a single collection **Learning:** `build_rule_metadata` derives exactly two collections, `owasp` and `cwe`, from the same references. Replacing its two generator traversals with one explicit loop reduces element visits from about 2N to N. Both versions remain O(N), so this is a constant-factor optimization rather than an asymptotic complexity improvement. **Action:** Combine repeated traversal when fixed derived collections share one source, while preserving ordering and classification semantics. Benchmark the production hot path before claiming a material wall-clock improvement. + +## 2026-10-05 - 제너레이터 대신 컴프리헨션 사용 최적화 +**Learning:** Python에서 tuple()이나 dict.fromkeys()에 제너레이터를 전달하면 오버헤드가 크므로, 리스트나 딕셔너리 컴프리헨션을 사용하여 C 속도로 처리해야 한다. +**Action:** Hot path에서는 제너레이터 대신 [ ]나 { }로 감싼 컴프리헨션을 tuple() 생성자 등에 전달한다. diff --git a/appguardrail_core/findings.py b/appguardrail_core/findings.py index 5e0c28b43..87c563d36 100644 --- a/appguardrail_core/findings.py +++ b/appguardrail_core/findings.py @@ -106,10 +106,10 @@ def normalize_findings( snippet_max_len: int = 400, ) -> tuple[dict[str, Any], ...]: """Normalize a finding collection into a stable tuple.""" - return tuple( + return tuple([ normalize_finding(finding, snippet_max_len=snippet_max_len) for finding in findings - ) + ]) def severity_counts(findings: Iterable[dict[str, Any]]) -> dict[str, int]: diff --git a/appguardrail_core/rules.py b/appguardrail_core/rules.py index c06ea203a..a616f1794 100644 --- a/appguardrail_core/rules.py +++ b/appguardrail_core/rules.py @@ -106,12 +106,10 @@ def as_dict(self) -> dict[str, Any]: def extract_public_references(message: str) -> tuple[str, ...]: """Extract OWASP, CWE, and CVE references already embedded in rule copy.""" - return tuple( - dict.fromkeys( - " ".join(match.group(1).split()) - for match in REFERENCE_RE.finditer(message or "") - ) - ) + return tuple({ + " ".join(match.group(1).split()): None + for match in REFERENCE_RE.finditer(message or "") + }) def _category_for_references(references: tuple[str, ...], fallback: str) -> str: @@ -174,8 +172,7 @@ def validate_rule_metadata(metadata: RuleMetadata | dict[str, Any]) -> list[str] def _merge_references(*groups: tuple[str, ...]) -> tuple[str, ...]: - return tuple( - dict.fromkeys( - reference for group in groups for reference in group if reference - ) - ) + return tuple({ + reference: None + for group in groups for reference in group if reference + })