diff --git a/.jules/bolt.md b/.jules/bolt.md index c5df166956..bead324d0e 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -48,3 +48,8 @@ **Context:** Guest MMU / `VirtualMemory.cs` (`CopyFromRegions`, `CopyToRegions`) **Learning:** Sequential tracking over Spans with an external `copied` counter and range indexer (`span[copied..]`) introduces local bounds check validation overhead. Reassigning the span reference in place via `span = span.Slice(chunkLength)` eliminates this and simplifies hot loops into `!span.IsEmpty` checks. **Action:** Use idiomatic slice-reassignment (`span = span.Slice(length)`) instead of external integer trackers in loop conditions over `Span` sequential memory operations to reduce arithmetic overhead. + +## 2026-10-06 - Zero-Allocation Search in KernelMemoryCompatExports +**Context:** src/SharpEmu.Libs/Kernel/KernelMemoryCompatExports.cs +**Learning:** Found an O(N log N) LINQ-based sorting/filtering bottleneck (`.Values.Where(...).OrderBy(...)`) inside the `KernelDirectMemoryQuery` lock. This was causing GC pressure due to enumerator and delegate allocations on a hot path during kernel HLE queries. +**Action:** Replaced it with an O(N) allocation-free manual search loop using standard variables (`matchStart`, `matchEnd`) to natively handle uniqueness and prioritization. diff --git a/src/SharpEmu.Libs/Kernel/KernelMemoryCompatExports.cs b/src/SharpEmu.Libs/Kernel/KernelMemoryCompatExports.cs index 15ee4bcd7e..14c62fb0fe 100644 --- a/src/SharpEmu.Libs/Kernel/KernelMemoryCompatExports.cs +++ b/src/SharpEmu.Libs/Kernel/KernelMemoryCompatExports.cs @@ -3706,19 +3706,23 @@ public static int KernelDirectMemoryQuery(CpuContext ctx) lock (_memoryGate) { - var candidates = _directAllocations.Values - .Where(block => findNext + // Performance optimization: Replaced O(N log N) LINQ sorting and allocating Where/OrderBy chain with a single-pass O(N) allocation-free manual search loop. + foreach (var block in _directAllocations.Values) + { + var isMatch = findNext ? block.Start + block.Length > offset - : offset >= block.Start && offset < block.Start + block.Length) - .OrderBy(block => block.Start); + : offset >= block.Start && offset < block.Start + block.Length; - foreach (var block in candidates) - { - found = true; - matchStart = block.Start; - matchEnd = block.Start + block.Length; - matchMemoryType = block.MemoryType; - break; + if (isMatch) + { + if (!found || block.Start < matchStart) + { + found = true; + matchStart = block.Start; + matchEnd = block.Start + block.Length; + matchMemoryType = block.MemoryType; + } + } } }