Skip to content

vpn: reduce throughputTracker per-tick allocations on mobile#498

Draft
garmr-ulfr wants to merge 1 commit into
mainfrom
throughput-tracker-opt
Draft

vpn: reduce throughputTracker per-tick allocations on mobile#498
garmr-ulfr wants to merge 1 commit into
mainfrom
throughput-tracker-opt

Conversation

@garmr-ulfr
Copy link
Copy Markdown
Collaborator

This pull request introduces optimizations to the throughput tracking logic in the VPN package, primarily to reduce memory allocations and garbage collection overhead, especially on mobile devices. The changes dynamically adjust the throughput sampling interval based on the platform and introduce scratch maps that are reused across sampling ticks to minimize allocations.

Performance improvements for throughput tracking:

  • The default throughput sampling interval (defaultThroughputSampleInterval) is now longer on mobile devices (3 seconds instead of 1 second) to reduce GC pressure caused by frequent allocations during heavy traffic. This is determined using the new common.IsMobile() check.
  • The throughputTracker struct now includes scratch maps (nextSeen, nextPerOutbound, and deltas) that are reused across sampling ticks, avoiding repeated allocations and reducing GC churn. [1] [2]

Refactoring of sampling logic:

  • The sample method now clears and reuses the scratch maps instead of allocating new ones each tick. The method also swaps the maps after each sampling tick to maintain correct state while minimizing allocations. [1] [2]

Other minor changes:

  • The time package import was removed from vpn/clash.go as it is no longer needed.
  • The newClashServer function now passes 0 for the throughput tracker interval, delegating the interval selection to the tracker itself.
  • Added import of github.com/getlantern/radiance/common to support platform detection.

Heap profiling on the iOS extension showed throughputTracker.sample as the
dominant cumulative allocator under traffic. Two changes:

- Reuse the deltas, nextSeen, and nextPerOutbound maps across ticks via
  clear+swap instead of allocating fresh maps each second.
- Default to a 3s sample interval on mobile. Each tick allocates a fresh
  snapshot slice of every live and recently-closed connection via the
  upstream trafficontrol API; at 1 Hz on a phone this dominated GC churn.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR optimizes VPN throughput tracking to reduce per-tick allocations/GC pressure (notably on mobile) by (1) selecting a longer default sampling interval on mobile platforms and (2) reusing scratch maps across sampling ticks instead of allocating new maps each tick.

Changes:

  • Make the default throughput sampling interval platform-aware (3s on mobile, 1s elsewhere) via common.IsMobile().
  • Rework throughputTracker.sample() to reuse and swap preallocated scratch maps (nextSeen, nextPerOutbound, deltas) to reduce allocations.
  • Update the Clash server wiring to pass 0 as the interval so the tracker selects its default interval.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
vpn/throughput_tracker.go Adds mobile-aware default interval and scratch-map reuse/swapping to reduce allocations per sampling tick.
vpn/clash.go Removes now-unused time import and delegates tracker interval selection by passing 0.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread vpn/throughput_tracker.go
Comment on lines +23 to +27
var defaultThroughputSampleInterval = func() time.Duration {
if common.IsMobile() {
return 3 * time.Second
}
return time.Second
Comment thread vpn/clash.go
Comment on lines 69 to +72
trafficManager: trafficManager,
throughputTracker: newThroughputTracker(trafficManager, time.Second),
modeList: modeList,
mode: initial,
throughputTracker: newThroughputTracker(trafficManager, 0),
modeList: modeList,
mode: initial,
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.

2 participants