This is an automated email from the ASF dual-hosted git repository.

Alanxtl pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/dubbo-go.git


The following commit(s) were added to refs/heads/develop by this push:
     new 0139a9bad perf(loadbalance): reduce temporary selection allocations 
(#3415)
0139a9bad is described below

commit 0139a9bad92b941b93fed8bce08e2e7389a6f9e7
Author: ヴァニラシ <[email protected]>
AuthorDate: Sun Jun 14 15:20:23 2026 +0800

    perf(loadbalance): reduce temporary selection allocations (#3415)
---
 cluster/loadbalance/aliasmethod/alias_method.go    | 25 +++++++++--
 cluster/loadbalance/leastactive/loadbalance.go     | 28 +++++++++----
 cluster/loadbalance/loadbalance_benchmarks_test.go | 48 ++++++++++++++++++++++
 cluster/loadbalance/random/loadbalance.go          | 16 +++++++-
 4 files changed, 105 insertions(+), 12 deletions(-)

diff --git a/cluster/loadbalance/aliasmethod/alias_method.go 
b/cluster/loadbalance/aliasmethod/alias_method.go
index cba727af1..f6335e671 100644
--- a/cluster/loadbalance/aliasmethod/alias_method.go
+++ b/cluster/loadbalance/aliasmethod/alias_method.go
@@ -28,6 +28,8 @@ import (
        "dubbo.apache.org/dubbo-go/v3/protocol/base"
 )
 
+const smallInvokerThreshold = 32
+
 type aliasMethodPicker struct {
        invokers []base.Invoker // Instance
 
@@ -47,15 +49,30 @@ func NewAliasMethodPicker(invokers []base.Invoker, 
invocation base.Invocation) *
 // Alias Method: https://en.wikipedia.org/wiki/Alias_method
 func (am *aliasMethodPicker) init(invocation base.Invocation) {
        n := len(am.invokers)
-       weights := make([]int64, n)
        am.alias = make([]int, n)
        am.prob = make([]float64, n)
 
        totalWeight := int64(0)
 
-       scaledProb := make([]float64, n)
-       small := make([]int, 0, n)
-       large := make([]int, 0, n)
+       var (
+               weightStack     [smallInvokerThreshold]int64
+               scaledProbStack [smallInvokerThreshold]float64
+               smallStack      [smallInvokerThreshold]int
+               largeStack      [smallInvokerThreshold]int
+       )
+       weights := weightStack[:]
+       scaledProb := scaledProbStack[:]
+       small := smallStack[:0]
+       large := largeStack[:0]
+       if n > smallInvokerThreshold {
+               weights = make([]int64, n)
+               scaledProb = make([]float64, n)
+               small = make([]int, 0, n)
+               large = make([]int, 0, n)
+       } else {
+               weights = weights[:n]
+               scaledProb = scaledProb[:n]
+       }
 
        now := time.Now().Unix()
        for i, invoker := range am.invokers {
diff --git a/cluster/loadbalance/leastactive/loadbalance.go 
b/cluster/loadbalance/leastactive/loadbalance.go
index 27c8f7f52..4dd37316c 100644
--- a/cluster/loadbalance/leastactive/loadbalance.go
+++ b/cluster/loadbalance/leastactive/loadbalance.go
@@ -32,6 +32,9 @@ import (
 const (
        // Key is used to set the load balance extension
        Key = "leastactive"
+
+       minStackInvokerCount = 8
+       maxStackInvokerCount = 32
 )
 
 func init() {
@@ -58,14 +61,25 @@ func (lb *leastActiveLoadBalance) Select(invokers 
[]base.Invoker, invocation bas
        }
 
        var (
-               leastActive  int32                  = -1 // The least active 
value of all invokers
-               totalWeight  int64                       // The number of 
invokers having the same least active value (LEAST_ACTIVE)
-               firstWeight  int64                       // Initial value, used 
for comparison
-               leastCount   int                         // The number of 
invokers having the same least active value (LEAST_ACTIVE)
-               leastIndexes = make([]int, count)        // The index of 
invokers having the same least active value (LEAST_ACTIVE)
-               sameWeight   = true                      // Every invoker has 
the same weight value?
-               weights      = make([]int64, count)      // The weight of every 
invokers
+               leastActive  int32   = -1 // The least active value of all 
invokers
+               totalWeight  int64        // Sum of weights of invokers having 
the same least active value (LEAST_ACTIVE)
+               firstWeight  int64        // Initial value, used for comparison
+               leastCount   int          // The number of invokers having the 
same least active value (LEAST_ACTIVE)
+               leastIndexes []int        // The index of invokers having the 
same least active value (LEAST_ACTIVE)
+               sameWeight   = true       // Every invoker has the same weight 
value?
+               weights      []int64      // The weight of every invokers
+       )
+       var (
+               leastIndexStack [maxStackInvokerCount]int
+               weightStack     [maxStackInvokerCount]int64
        )
+       if count >= minStackInvokerCount && count <= maxStackInvokerCount {
+               leastIndexes = leastIndexStack[:count]
+               weights = weightStack[:count]
+       } else {
+               leastIndexes = make([]int, count)
+               weights = make([]int64, count)
+       }
 
        now := time.Now().Unix()
        for i := 0; i < count; i++ {
diff --git a/cluster/loadbalance/loadbalance_benchmarks_test.go 
b/cluster/loadbalance/loadbalance_benchmarks_test.go
index f8a483895..612d31ea0 100644
--- a/cluster/loadbalance/loadbalance_benchmarks_test.go
+++ b/cluster/loadbalance/loadbalance_benchmarks_test.go
@@ -48,6 +48,19 @@ func Generate() []base.Invoker {
        return invokers
 }
 
+func generateInvokers(count int, weighted bool) []base.Invoker {
+       invokers := make([]base.Invoker, 0, count)
+       for i := 1; i <= count; i++ {
+               rawURL := 
fmt.Sprintf("dubbo://192.168.1.%v:20000/org.apache.demo.HelloService", i)
+               if weighted {
+                       rawURL = fmt.Sprintf("%s?weight=%d", rawURL, i)
+               }
+               url, _ := common.NewURL(rawURL)
+               invokers = append(invokers, base.NewBaseInvoker(url))
+       }
+       return invokers
+}
+
 func Benchloadbalance(b *testing.B, lb loadbalance.LoadBalance) {
        b.Helper()
        invokers := Generate()
@@ -106,3 +119,38 @@ func BenchmarkGetWeightAt(b *testing.B) {
                loadbalance.GetWeightAt(invokers[i%len(invokers)], inv, now)
        }
 }
+
+func benchmarkLoadBalanceSmallMedium(b *testing.B, lb loadbalance.LoadBalance) 
{
+       b.Helper()
+       for _, count := range []int{2, 4, 8, 16, 32, 33} {
+               for _, weighted := range []bool{false, true} {
+                       name := fmt.Sprintf("invokers=%d", count)
+                       if weighted {
+                               name += "/weighted"
+                       } else {
+                               name += "/uniform"
+                       }
+                       b.Run(name, func(b *testing.B) {
+                               invokers := generateInvokers(count, weighted)
+                               rpcInvocation := &invocation.RPCInvocation{}
+                               b.ReportAllocs()
+                               b.ResetTimer()
+                               for i := 0; i < b.N; i++ {
+                                       lb.Select(invokers, rpcInvocation)
+                               }
+                       })
+               }
+       }
+}
+
+func BenchmarkRandomLoadbalanceSmallMedium(b *testing.B) {
+       benchmarkLoadBalanceSmallMedium(b, 
extension.GetLoadbalance(constant.LoadBalanceKeyRandom))
+}
+
+func BenchmarkLeastactiveLoadbalanceSmallMedium(b *testing.B) {
+       benchmarkLoadBalanceSmallMedium(b, 
extension.GetLoadbalance(constant.LoadBalanceKeyLeastActive))
+}
+
+func BenchmarkAliasMethodLoadbalanceSmallMedium(b *testing.B) {
+       benchmarkLoadBalanceSmallMedium(b, 
extension.GetLoadbalance(constant.LoadBalanceKeyAliasMethod))
+}
diff --git a/cluster/loadbalance/random/loadbalance.go 
b/cluster/loadbalance/random/loadbalance.go
index 43ce7ee81..3c80a4781 100644
--- a/cluster/loadbalance/random/loadbalance.go
+++ b/cluster/loadbalance/random/loadbalance.go
@@ -53,7 +53,21 @@ func (lb *randomLoadBalance) Select(invokers []base.Invoker, 
invocation base.Inv
        // Every invoker has the same weight?
        sameWeight := true
        // the maxWeight of every invokers, the minWeight = 0 or the maxWeight 
of the last invoker
-       weights := make([]int64, length)
+       // Use stack buffers for common small invoker lists to avoid the 
temporary weights slice allocation.
+       var weights []int64
+       switch {
+       case length <= 8:
+               var weightStack [8]int64
+               weights = weightStack[:length:length]
+       case length <= 16:
+               var weightStack [16]int64
+               weights = weightStack[:length:length]
+       case length <= 32:
+               var weightStack [32]int64
+               weights = weightStack[:length:length]
+       default:
+               weights = make([]int64, length)
+       }
        // The sum of weights
        var totalWeight int64 = 0
 

Reply via email to