Skip to content

store lighthouses as a slice - #1473

Merged
JackDoan merged 2 commits into
masterfrom
is-lighthouse-speedup
Sep 10, 2025
Merged

store lighthouses as a slice#1473
JackDoan merged 2 commits into
masterfrom
is-lighthouse-speedup

Conversation

@JackDoan

@JackDoan JackDoan commented Sep 8, 2025

Copy link
Copy Markdown
Collaborator

Benchmark Results

[jack@jackdesk bench]$ go test -bench=. 
goos: linux
goarch: amd64
pkg: github.com/slackhq/nebula/bench
cpu: AMD Ryzen Threadripper 7970X 32-Cores          
BenchmarkSlice8Hit-64           685424707                1.578 ns/op
BenchmarkSlice8Miss-64          360156667                3.276 ns/op
BenchmarkSlice16Hit-64          362109838                3.310 ns/op
BenchmarkSlice16Miss-64         182394097                6.180 ns/op
BenchmarkSlice32Hit-64          245161548                4.868 ns/op
BenchmarkSlice32Miss-64         86850042                12.17 ns/op
BenchmarkSlice64Hit-64          93207307                12.56 ns/op
BenchmarkSlice64Miss-64         65263791                18.12 ns/op
BenchmarkArray8Hit-64           532953376                2.249 ns/op
BenchmarkArray8Miss-64          359236872                3.298 ns/op
BenchmarkArray16Hit-64          334303156                3.577 ns/op
BenchmarkArray16Miss-64         183460712                6.475 ns/op
BenchmarkArray32Hit-64          192244377                6.200 ns/op
BenchmarkArray32Miss-64         90010026                12.55 ns/op
BenchmarkArray64Hit-64          84817359                12.86 ns/op
BenchmarkArray64Miss-64         47053088                24.15 ns/op
BenchmarkMap8Hit-64             83778686                13.09 ns/op
BenchmarkMap8Miss-64            120439582                9.971 ns/op
BenchmarkMap16Hit-64            77629213                14.14 ns/op
BenchmarkMap32Hit-64            77929177                14.08 ns/op
BenchmarkMap64Hit-64            75726654                14.28 ns/op
BenchmarkBart8Hit-64            164469320                7.282 ns/op
BenchmarkBart8Miss-64           319318362                3.754 ns/op
BenchmarkBart16Hit-64           163937019                7.341 ns/op
BenchmarkBart16Miss-64          320770320                3.745 ns/op
BenchmarkBart32Hit-64           125632747                9.596 ns/op
BenchmarkBart32Miss-64          320097038                3.746 ns/op
BenchmarkBart64Hit-64           163630293                7.296 ns/op
BenchmarkBart64Miss-64          149423208                8.034 ns/op
PASS
ok      github.com/slackhq/nebula/bench 44.271s

Benchmark

package main

import (
	"math/rand"
	"net/netip"
	"testing"

	"github.com/gaissmai/bart"
)

// Generate test data
func generateAddrs(n int) []netip.Addr {
	addrs := make([]netip.Addr, n)
	for i := 0; i < n; i++ {
		// Generate random IPv4 addresses
		a := byte(rand.Intn(256))
		b := byte(rand.Intn(256))
		c := byte(rand.Intn(256))
		d := byte(rand.Intn(256))
		addrs[i] = netip.AddrFrom4([4]byte{a, b, c, d})
	}
	return addrs
}

// Slice-based linear search
func sliceContains(addrs []netip.Addr, target netip.Addr) bool {
	for _, addr := range addrs {
		if addr == target {
			return true
		}
	}
	return false
}

type AddrArray [64]netip.Addr

func (a *AddrArray) contains(target netip.Addr, count int) bool {
	for i := 0; i < count; i++ {
		if a[i] == target {
			return true
		}
	}
	return false
}

// Map-based lookup
func mapContains(addrMap map[netip.Addr]struct{}, target netip.Addr) bool {
	_, exists := addrMap[target]
	return exists
}

// Benchmark setup
var (
	testAddrs8  = generateAddrs(8)
	testAddrs16 = generateAddrs(16)
	testAddrs32 = generateAddrs(32)
	testAddrs64 = generateAddrs(64)

	// Target addresses (mix of hits and misses)
	targetHit  = testAddrs32[15]                        // guaranteed hit
	targetMiss = netip.MustParseAddr("192.168.255.255") // likely miss
)

// Setup data structures
func setupMap(addrs []netip.Addr) map[netip.Addr]struct{} {
	//m := make(map[netip.Addr]struct{}, len(addrs))
	m := make(map[netip.Addr]struct{})
	for _, addr := range addrs {
		m[addr] = struct{}{}
	}
	return m
}

func setupArray(addrs []netip.Addr) *AddrArray {
	var arr AddrArray
	copy(arr[:], addrs)
	return &arr
}

func benchSlice(b *testing.B, x []netip.Addr, target netip.Addr) {
	for i := 0; i < b.N; i++ {
		sliceContains(x, target)
	}
}

func benchBart(b *testing.B, x []netip.Addr, target netip.Addr) {
	blart := bart.Lite{}
	for _, addr := range x {
		blart.Insert(netip.PrefixFrom(addr, 32))
	}
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		blart.Lookup(target)
	}
}

func BenchmarkSlice8Hit(b *testing.B) {
	benchSlice(b, testAddrs8, testAddrs8[4])
}

func BenchmarkSlice8Miss(b *testing.B) {
	benchSlice(b, testAddrs8, targetMiss)
}

func BenchmarkSlice16Hit(b *testing.B) {
	benchSlice(b, testAddrs16, testAddrs16[8])
}

func BenchmarkSlice16Miss(b *testing.B) {
	benchSlice(b, testAddrs16, targetMiss)
}

func BenchmarkSlice32Hit(b *testing.B) {
	benchSlice(b, testAddrs32, testAddrs32[16])
}

func BenchmarkSlice32Miss(b *testing.B) {
	benchSlice(b, testAddrs32, targetMiss)
}

func BenchmarkSlice64Hit(b *testing.B) {
	benchSlice(b, testAddrs64, testAddrs64[32])
}

func BenchmarkSlice64Miss(b *testing.B) {
	benchSlice(b, testAddrs64, targetMiss)
}

func BenchmarkArray8Hit(b *testing.B) {
	arr := setupArray(testAddrs8)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		arr.contains(testAddrs8[4], len(testAddrs8))
	}
}

func BenchmarkArray8Miss(b *testing.B) {
	arr := setupArray(testAddrs8)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		arr.contains(targetMiss, len(testAddrs8))
	}
}

func BenchmarkArray16Hit(b *testing.B) {
	arr := setupArray(testAddrs16)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		arr.contains(testAddrs16[8], len(testAddrs16))
	}
}

func BenchmarkArray16Miss(b *testing.B) {
	arr := setupArray(testAddrs16)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		arr.contains(targetMiss, len(testAddrs16))
	}
}

func BenchmarkArray32Hit(b *testing.B) {
	arr := setupArray(testAddrs32)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		arr.contains(targetHit, len(testAddrs32))
	}
}

func BenchmarkArray32Miss(b *testing.B) {
	arr := setupArray(testAddrs32)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		arr.contains(targetMiss, len(testAddrs32))
	}
}

func BenchmarkArray64Hit(b *testing.B) {
	arr := setupArray(testAddrs64)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		arr.contains(testAddrs64[32], len(testAddrs64))
	}
}

func BenchmarkArray64Miss(b *testing.B) {
	arr := setupArray(testAddrs64)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		arr.contains(targetMiss, len(testAddrs64))
	}
}

func BenchmarkMap8Hit(b *testing.B) {
	m := setupMap(testAddrs8)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		mapContains(m, testAddrs8[4])
	}
}

func BenchmarkMap8Miss(b *testing.B) {
	m := setupMap(testAddrs8)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		mapContains(m, targetMiss)
	}
}

func BenchmarkMap16Hit(b *testing.B) {
	m := setupMap(testAddrs16)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		mapContains(m, testAddrs16[8])
	}
}

func BenchmarkMap32Hit(b *testing.B) {
	m := setupMap(testAddrs32)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		mapContains(m, targetHit)
	}
}

func BenchmarkMap64Hit(b *testing.B) {
	m := setupMap(testAddrs64)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		mapContains(m, testAddrs64[32])
	}
}

func BenchmarkBart8Hit(b *testing.B) {
	benchBart(b, testAddrs8, testAddrs8[4])
}

func BenchmarkBart8Miss(b *testing.B) {
	benchBart(b, testAddrs8, targetMiss)
}

func BenchmarkBart16Hit(b *testing.B) {
	benchBart(b, testAddrs16, testAddrs16[8])
}

func BenchmarkBart16Miss(b *testing.B) {
	benchBart(b, testAddrs16, targetMiss)
}

func BenchmarkBart32Hit(b *testing.B) {
	benchBart(b, testAddrs32, testAddrs32[16])
}

func BenchmarkBart32Miss(b *testing.B) {
	benchBart(b, testAddrs32, targetMiss)
}

func BenchmarkBart64Hit(b *testing.B) {
	benchBart(b, testAddrs64, testAddrs64[32])
}

func BenchmarkBart64Miss(b *testing.B) {
	benchBart(b, testAddrs64, targetMiss)
}

…and fewer than 16 vpnaddrs on a host, I guess), it's faster
Comment thread lighthouse.go
_, ok := lh.GetLighthouses()[vpnAddr]
return ok
l := lh.GetLighthouses()
for i := range l {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Might be ever so slightly slower but slices.Contains is there.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

it's meaningfully slower, at least in my testing 😭

@JackDoan
JackDoan merged commit 8196c22 into master Sep 10, 2025
9 checks passed
@JackDoan
JackDoan deleted the is-lighthouse-speedup branch September 10, 2025 14:43
@wadey wadey added this to the v1.10.0 milestone Sep 10, 2025
@nbrownus nbrownus mentioned this pull request Nov 19, 2025
63 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants