Skip to content

Optimize peer ranking paths in prioritized peers and worse connections#1045

Open
Sahil-4555 wants to merge 2 commits into
anacrolix:masterfrom
Sahil-4555:perf/optimize-peer-ranking-paths
Open

Optimize peer ranking paths in prioritized peers and worse connections#1045
Sahil-4555 wants to merge 2 commits into
anacrolix:masterfrom
Sahil-4555:perf/optimize-peer-ranking-paths

Conversation

@Sahil-4555
Copy link
Copy Markdown

This update affects two ranking-heavy paths: prioritized peer insertion and worse-connection selection.

  • Peer ranking now caches the address hash at insertion time, employs direct comparison logic rather than the generic comparison builder, and handles common address types more efficiently by writing their canonical address form directly into the hash rather than creating an intermediate string first.
  • Connection ranking now use direct field comparisons, stores ranking inputs in contiguous storage to decrease allocation overhead, and memoizes peer-priority lookups using a closure-based helper to prevent redundant work and address copylock concerns.
  • The relevant tests were expanded to include the address-hash path, as well as the priority-error and panic edge cases in worse-connection ordering.

@anacrolix
Copy link
Copy Markdown
Owner

Can you include a benchmark?

@anacrolix anacrolix self-assigned this Apr 13, 2026
@Sahil-4555
Copy link
Copy Markdown
Author

PrioritizedPeers Benchmark

func benchmarkPrioritizedPeers(n int, addrForIndex func(int) PeerRemoteAddr) []PeerInfo {
	peers := make([]PeerInfo, n)
	for i := range peers {
		peers[i] = PeerInfo{
			Addr:    addrForIndex(i),
			Trusted: i%7 == 0,
		}
	}
	return peers
}

func BenchmarkPrioritizedPeersAdd(b *testing.B) {
	benchmarks := []struct {
		name         string
		addrForIndex func(int) PeerRemoteAddr
	}{
		{
			name: "IPv4IPPortAddr",
			addrForIndex: func(i int) PeerRemoteAddr {
				return ipPortAddr{
					IP:   net.IPv4(192, 0, 2, byte((i%250)+1)),
					Port: 10000 + i,
				}
			},
		},
		{
			name: "IPv6IPPortAddr",
			addrForIndex: func(i int) PeerRemoteAddr {
				return ipPortAddr{
					IP:   net.ParseIP(fmt.Sprintf("2001:db8::%x", i+1)),
					Port: 10000 + i,
				}
			},
		},
		{
			name: "StringAddr",
			addrForIndex: func(i int) PeerRemoteAddr {
				return StringAddr(
					net.JoinHostPort(
						net.IPv4(192, 0, 2, byte((i%250)+1)).String(),
						fmt.Sprintf("%d", 10000+i),
					),
				)
			},
		},
		{
			name: "NetipAddrPort",
			addrForIndex: func(i int) PeerRemoteAddr {
				return netip.AddrPortFrom(
					netip.AddrFrom4([4]byte{192, 0, 2, byte((i % 250) + 1)}),
					uint16(10000+i),
				)
			},
		},
	}
	for _, bench := range benchmarks {
		b.Run(bench.name, func(b *testing.B) {
			peers := benchmarkPrioritizedPeers(512, bench.addrForIndex)
			b.ReportAllocs()
			for i := 0; i < b.N; i++ {
				pp := prioritizedPeers{
					om: btree.New(8),
					getPrio: func(p PeerInfo) peerPriority {
						return peerPriority(len(p.Addr.String()))
					},
				}
				for _, p := range peers {
					pp.Add(p)
				}
			}
		})
	}
}

WorseConn Benchmark

func BenchmarkWorseConnLess(b *testing.B) {
	b.Run("BadDirection", func(b *testing.B) {
		left := worseConnInput{
			BadDirection:    true,
			GetPeerPriority: func() (peerPriority, error) { return 5, nil },
			Pointer:         1,
		}
		right := worseConnInput{
			GetPeerPriority: func() (peerPriority, error) { return 7, nil },
			Pointer:         2,
		}
		b.ReportAllocs()
		for i := 0; i < b.N; i++ {
			if !left.Less(&right) {
				b.Fatal("unexpected ordering")
			}
		}
	})
	b.Run("TimestampTieBreak", func(b *testing.B) {
		left := worseConnInput{
			LastHelpful:     time.Unix(100, 0),
			GetPeerPriority: func() (peerPriority, error) { return 5, nil },
			Pointer:         1,
		}
		right := worseConnInput{
			LastHelpful:     time.Unix(101, 0),
			GetPeerPriority: func() (peerPriority, error) { return 7, nil },
			Pointer:         2,
		}
		b.ReportAllocs()
		for i := 0; i < b.N; i++ {
			if !left.Less(&right) {
				b.Fatal("unexpected ordering")
			}
		}
	})
	b.Run("PeerPriority", func(b *testing.B) {
		left := worseConnInput{
			GetPeerPriority: benchmarkMemoizedPeerPriority(func() (peerPriority, error) { return 5, nil }),
			Pointer:         1,
		}
		right := worseConnInput{
			GetPeerPriority: benchmarkMemoizedPeerPriority(func() (peerPriority, error) { return 7, nil }),
			Pointer:         2,
		}
		b.ReportAllocs()
		for i := 0; i < b.N; i++ {
			if !left.Less(&right) {
				b.Fatal("unexpected ordering")
			}
		}
	})
}

func benchmarkMemoizedPeerPriority(f func() (peerPriority, error)) func() (peerPriority, error) {
	var once sync.Once
	var prio peerPriority
	var err error
	return func() (peerPriority, error) {
		once.Do(func() {
			prio, err = f()
		})
		return prio, err
	}
}

Result

goos: linux
goarch: amd64
pkg: github.com/anacrolix/torrent
cpu: Intel(R) Core(TM) i7-8565U CPU @ 1.80GHz
                                   │   old.txt    │               new.txt               │
                                   │    sec/op    │   sec/op     vs base                │
PrioritizedPeersAdd/IPv4IPPortAddr   2580.4µ ± 7%   441.2µ ± 6%  -82.90% (p=0.000 n=10)
PrioritizedPeersAdd/IPv6IPPortAddr   3874.0µ ± 4%   562.5µ ± 2%  -85.48% (p=0.000 n=10)
PrioritizedPeersAdd/StringAddr        566.2µ ± 3%   273.2µ ± 2%  -51.74% (p=0.000 n=10)
PrioritizedPeersAdd/NetipAddrPort    1372.2µ ± 4%   348.5µ ± 3%  -74.61% (p=0.000 n=10)
WorseConnLess/BadDirection           33.580n ± 3%   2.506n ± 5%  -92.54% (p=0.000 n=10)
WorseConnLess/TimestampTieBreak      34.375n ± 6%   9.195n ± 3%  -73.25% (p=0.000 n=10)
WorseConnLess/PeerPriority            37.11n ± 4%   19.54n ± 3%  -47.35% (p=0.000 n=10)
geomean                               16.50µ        3.761µ       -77.21%

                                   │     old.txt     │                new.txt                 │
                                   │      B/op       │     B/op      vs base                  │
PrioritizedPeersAdd/IPv4IPPortAddr   457.19Ki ± 0%     97.13Ki ± 0%  -78.75% (p=0.000 n=10)
PrioritizedPeersAdd/IPv6IPPortAddr    557.8Ki ± 0%     103.5Ki ± 0%  -81.45% (p=0.000 n=10)
PrioritizedPeersAdd/StringAddr        70.17Ki ± 0%     70.77Ki ± 0%   +0.86% (p=0.000 n=10)
PrioritizedPeersAdd/NetipAddrPort    289.12Ki ± 0%     81.13Ki ± 0%  -71.94% (p=0.000 n=10)
WorseConnLess/BadDirection              0.000 ± 0%       0.000 ± 0%        ~ (p=1.000 n=10) ¹
WorseConnLess/TimestampTieBreak         0.000 ± 0%       0.000 ± 0%        ~ (p=1.000 n=10) ¹
WorseConnLess/PeerPriority              0.000 ± 0%       0.000 ± 0%        ~ (p=1.000 n=10) ¹
geomean                                            ²                 -47.39%                ²
¹ all samples are equal
² summaries must be >0 to compute geomean

                                   │    old.txt     │                new.txt                │
                                   │   allocs/op    │  allocs/op   vs base                  │
PrioritizedPeersAdd/IPv4IPPortAddr   32.971k ± 0%     2.758k ± 0%  -91.64% (p=0.000 n=10)
PrioritizedPeersAdd/IPv6IPPortAddr   33.661k ± 0%     2.764k ± 0%  -91.79% (p=0.000 n=10)
PrioritizedPeersAdd/StringAddr         708.0 ± 0%      710.0 ± 0%   +0.28% (p=0.000 n=10)
PrioritizedPeersAdd/NetipAddrPort    11.461k ± 0%     1.222k ± 0%  -89.34% (p=0.000 n=10)
WorseConnLess/BadDirection             0.000 ± 0%      0.000 ± 0%        ~ (p=1.000 n=10) ¹
WorseConnLess/TimestampTieBreak        0.000 ± 0%      0.000 ± 0%        ~ (p=1.000 n=10) ¹
WorseConnLess/PeerPriority             0.000 ± 0%      0.000 ± 0%        ~ (p=1.000 n=10) ¹
geomean                                           ²                -64.33%                ²
¹ all samples are equal
² summaries must be >0 to compute geomean

@Sahil-4555
Copy link
Copy Markdown
Author

Can you include a benchmark?

Done. Added

@anacrolix
Copy link
Copy Markdown
Owner

Nice

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