From 14e013134938efc6bd24eb32eb60e53264f05258 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Mon, 5 Oct 2026 14:35:43 +0000 Subject: [PATCH] perf: optimize SARIF rule index lookup --- .jules/bolt.md | 3 +++ appguardrail_core/sarif.py | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/.jules/bolt.md b/.jules/bolt.md index 8ce7ce8f7..ce5215398 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -77,3 +77,6 @@ ## 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. +## 2025-02-12 - O(1) SARIF Rule Index Lookup Optimization +**Learning:** In Python, calling `list(dict).index(key)` inside a loop over data items causes severe O(N^2) performance degradation because it creates a new list and performs a linear search on every iteration. This pattern was found in `appguardrail_core/sarif.py` during SARIF generation. +**Action:** Always avoid `list(dict).index()` inside loops. Maintain a parallel index mapping dictionary (e.g., `indices[key] = len(indices)`) to achieve O(1) lookups and keep the overall loop bounded to O(N) time complexity. diff --git a/appguardrail_core/sarif.py b/appguardrail_core/sarif.py index ca4a9fb6b..43a74f7c8 100644 --- a/appguardrail_core/sarif.py +++ b/appguardrail_core/sarif.py @@ -39,12 +39,15 @@ def findings_to_sarif( normalized = normalize_findings(findings) rules: dict[str, dict[str, Any]] = {} + rule_indices: dict[str, int] = {} results: list[dict[str, Any]] = [] for f in normalized: rule_id = f["rule_id"] severity = f["severity"] refs = f.get("references") or () if rule_id not in rules: + # ⚡ Bolt: Maintain O(1) index lookup to avoid list(rules).index() O(N) overhead + rule_indices[rule_id] = len(rules) rule: dict[str, Any] = { "id": rule_id, "name": rule_id, @@ -68,7 +71,7 @@ def findings_to_sarif( results.append( { "ruleId": rule_id, - "ruleIndex": list(rules).index(rule_id), + "ruleIndex": rule_indices[rule_id], "level": _LEVEL.get(severity, "note"), "message": {"text": f["message"].strip()}, "locations": [