Skip to content

Fix O(n²) performance in ServerEventParser.parse()#49

Merged
Recouse merged 1 commit intoRecouse:mainfrom
liefran-sim:fix/parser-quadratic-performance
May 7, 2026
Merged

Fix O(n²) performance in ServerEventParser.parse()#49
Recouse merged 1 commit intoRecouse:mainfrom
liefran-sim:fix/parser-quadratic-performance

Conversation

@liefran-sim
Copy link
Copy Markdown
Contributor

Summary

  • Fixes quadratic performance in ServerEventParser.parse() that causes severe delays (15-20s) for large SSE events delivered in small chunks
  • Reduces complexity from O(n²) to O(n) by using in-place buffer append and tail-region-only separator scanning

Fixes #48

Problem

When receiving a large SSE event (e.g., 1.5MB) via URLSession's ~8KB didReceiveData callbacks:

  1. buffer + data creates a new Data allocation every call, copying the entire buffer
  2. splitBuffer scans the entire buffer for \n\n on every chunk, even when no separator is present

For ~180 chunks: total work = 8KB + 16KB + 24KB + ... + 1.5MB ≈ 135MB of copying/scanning.

Fix

  1. buffer.append(data) — in-place append, amortized O(1)
  2. Tail-region scan — only check the newly added bytes (+ small overlap for boundary-crossing separators) for \n\n
  3. Early return — skip splitBuffer entirely when no separator is detected (vast majority of calls)

Impact

Metric Before After
Per-chunk work O(buffer_size) O(chunk_size)
Overall complexity O(n²) O(n)
1.5MB event parse time ~15-20s <100ms

Testing

Verified in production iOS app with SSE flight search responses containing 200-400 inventories (~1.5MB payloads). Events that previously caused 30s timeout failures now parse in under 100ms.

The parse() method had quadratic performance when receiving large SSE events
delivered in small chunks (e.g., URLSession's ~8KB didReceiveData callbacks):

1. `buffer + data` created a new Data allocation on every call, copying the
   entire accumulated buffer each time.
2. `splitBuffer` scanned the entire buffer for separators on every chunk,
   even when no separator could possibly be present in the new data.

For a 1.5MB SSE event arriving in ~180 chunks, total work was ~135MB of
data copying/scanning, causing 15-20s delays that triggered timeouts.

Fix:
- Use `buffer.append(data)` for amortized O(1) in-place append
- Only scan the tail region (new data + separator overlap) for separators
- Only call splitBuffer when a separator is actually detected

This reduces per-chunk work from O(buffer_size) to O(chunk_size), making
the overall complexity O(n) instead of O(n²).

Fixes Recouse#48
Copy link
Copy Markdown
Owner

@Recouse Recouse left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like a solid change, thank you

@Recouse Recouse merged commit d3d3a91 into Recouse:main May 7, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

O(n²) performance in ServerEventParser.parse() causes event loss for large SSE payloads

2 participants