Skip to content

resolver: Make EndpointMap's Get, Set and Delete operations O(1) #8179

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Mar 19, 2025

Conversation

arjan-bal
Copy link
Contributor

@arjan-bal arjan-bal commented Mar 17, 2025

Fixes: #8173

This PR changes only the internal implementation of EndpointMap to use a map keyed by an unordered list of addresses. This avoids the need to iterate over the entire map to check the existence of a single element. This brings down the average case time complexity for Get, Set and Delete from O(n) to O(1) per operation, where n is the number of endpoints in the map.

Benchmarks

Code:

func BenchmarkEndpointMap(b *testing.B) {
	em := NewEndpointMap()
	for i := range b.N {
		em.Set(Endpoint{
			Addresses: []Address{{Addr: fmt.Sprintf("%d.%d.%d.%d", i, i, i, i)}},
		}, i)
	}
	for i := range b.N {
		em.Get(Endpoint{
			Addresses: []Address{{Addr: fmt.Sprintf("%d.%d.%d.%d", i, i, i, i)}},
		})
	}
	for i := range b.N {
		em.Delete(Endpoint{
			Addresses: []Address{{Addr: fmt.Sprintf("%d.%d.%d.%d", i, i, i, i)}},
		})
	}
}

Without any changes

goos: linux
goarch: amd64
pkg: google.golang.org/grpc/resolver
cpu: Intel(R) Xeon(R) CPU @ 2.60GHz
BenchmarkEndpointMap-48            10000            839907 ns/op
PASS

With base64 encoding the address list

goos: linux
goarch: amd64
pkg: google.golang.org/grpc/resolver
cpu: Intel(R) Xeon(R) CPU @ 2.60GHz
BenchmarkEndpointMap-48           373237              3770 ns/op
PASS

With JSON encoding the address list

goos: linux
goarch: amd64
pkg: google.golang.org/grpc/resolver
cpu: Intel(R) Xeon(R) CPU @ 2.60GHz
BenchmarkEndpointMap-48           289248              4275 ns/op
PASS

RELEASE NOTES:

  • resolver: EndpointMap's Get, Set and Delete operations are now O(1) on average.

@arjan-bal arjan-bal added this to the 1.72 Release milestone Mar 17, 2025
@arjan-bal arjan-bal added Type: Performance Performance improvements (CPU, network, memory, etc) Area: Resolvers/Balancers Includes LB policy & NR APIs, resolver/balancer/picker wrappers, LB policy impls and utilities. labels Mar 17, 2025
@arjan-bal arjan-bal force-pushed the efficient-endpoint-map branch from 78c0b88 to a24bee3 Compare March 17, 2025 23:57
@arjan-bal arjan-bal closed this Mar 18, 2025
@arjan-bal arjan-bal reopened this Mar 18, 2025
Copy link

codecov bot commented Mar 18, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 82.00%. Comparing base (1f6b0cf) to head (1887f0a).
Report is 5 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #8179      +/-   ##
==========================================
- Coverage   82.42%   82.00%   -0.43%     
==========================================
  Files         393      410      +17     
  Lines       39162    40233    +1071     
==========================================
+ Hits        32280    32993     +713     
- Misses       5571     5878     +307     
- Partials     1311     1362      +51     
Files with missing lines Coverage Δ
resolver/map.go 96.19% <100.00%> (-0.56%) ⬇️

... and 39 files with indirect coverage changes

🚀 New features to boost your workflow:
  • Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

en := toEndpointNode(e)
if endpoint := em.find(en); endpoint != nil {
return em.endpoints[endpoint], true
val, found := em.endpoints[encodeEndpoint(e)]
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is still O(n) in the number of addresses, no?

Copy link
Contributor Author

@arjan-bal arjan-bal Mar 18, 2025

Choose a reason for hiding this comment

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

encodeEndpoint is O(n) where n is the number of addresses within a single endpoint. Practically a single endpoint should contain around 2 addresses or so.

em.endpoints[x] is O(n) where n is also the number of addresses within a single endpoint. This is independent of the number of endpoints in the map, which can be much larger than n.

Copy link
Contributor

@easwars easwars left a comment

Choose a reason for hiding this comment

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

Do you think it makes sense to checkin some/all of the benchmark code that you have for posterity?

resolver/map.go Outdated
}
return nil, false
return nil, found
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: return nil, false here to be explicit about the second return value.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

@easwars easwars assigned arjan-bal and unassigned easwars Mar 18, 2025
@arjan-bal
Copy link
Contributor Author

Do you think it makes sense to checkin some/all of the benchmark code that you have for posterity?

Added the micro benchmark mentioned in the PR description.

@arjan-bal arjan-bal removed the request for review from dfawley March 18, 2025 23:14
@arjan-bal arjan-bal assigned arjan-bal and unassigned atollena Mar 19, 2025
@arjan-bal arjan-bal merged commit eb744de into grpc:master Mar 19, 2025
15 checks passed
@arjan-bal arjan-bal deleted the efficient-endpoint-map branch March 19, 2025 18:22
arjan-bal added a commit to arjan-bal/grpc-go that referenced this pull request Mar 19, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area: Resolvers/Balancers Includes LB policy & NR APIs, resolver/balancer/picker wrappers, LB policy impls and utilities. Type: Performance Performance improvements (CPU, network, memory, etc)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

SubChannels stuck in Idle when using Weighted Round Robin
4 participants