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

zeroshade pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/iceberg-go.git


The following commit(s) were added to refs/heads/main by this push:
     new 410b789ed perf(internal): stop propagating unchanged packing minimums 
(#2240)
410b789ed is described below

commit 410b789ed781852bbb4376e4f5cb57e76da12c56
Author: Minh Vu <[email protected]>
AuthorDate: Mon Oct 26 16:35:27 2026 +0100

    perf(internal): stop propagating unchanged packing minimums (#2240)
    
    Signed-off-by: Minh Vu <[email protected]>
---
 internal/packing_indexed.go             |   7 +-
 internal/packing_indexed_update_test.go | 114 ++++++++++++++++++++++++++++++++
 2 files changed, 120 insertions(+), 1 deletion(-)

diff --git a/internal/packing_indexed.go b/internal/packing_indexed.go
index 0cac74057..9f54a0faa 100644
--- a/internal/packing_indexed.go
+++ b/internal/packing_indexed.go
@@ -60,7 +60,12 @@ func (idx *packingWeightIndex) update(bin int, weight int64) 
{
        idx.weights[node] = weight
 
        for node /= 2; node > 0; node /= 2 {
-               idx.weights[node] = min(idx.weights[node*2], 
idx.weights[node*2+1])
+               minimum := min(idx.weights[node*2], idx.weights[node*2+1])
+               if idx.weights[node] == minimum {
+                       // An unchanged minimum cannot affect any ancestor.
+                       break
+               }
+               idx.weights[node] = minimum
        }
 }
 
diff --git a/internal/packing_indexed_update_test.go 
b/internal/packing_indexed_update_test.go
new file mode 100644
index 000000000..37b0d653a
--- /dev/null
+++ b/internal/packing_indexed_update_test.go
@@ -0,0 +1,114 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package internal
+
+import (
+       "fmt"
+       "math"
+       "math/rand"
+       "slices"
+       "strconv"
+       "testing"
+)
+
+// recomputeAllPackingMinimums preserves the old update implementation so the
+// test and benchmark can compare both paths without a separate checkout.
+func recomputeAllPackingMinimums(idx *packingWeightIndex, bin int, weight 
int64) {
+       node := idx.size + bin
+       idx.weights[node] = weight
+       for node /= 2; node > 0; node /= 2 {
+               idx.weights[node] = min(idx.weights[node*2], 
idx.weights[node*2+1])
+       }
+}
+
+func TestPackingWeightIndexUpdateMatchesFullRecompute(t *testing.T) {
+       for _, capacity := range []int{1, 2, 3, 7, 32, 128, 512, 2048} {
+               t.Run(strconv.Itoa(capacity), func(t *testing.T) {
+                       original := newPackingWeightIndex(capacity)
+                       optimized := newPackingWeightIndex(capacity)
+                       rng := rand.New(rand.NewSource(int64(capacity)))
+
+                       for step := range 2_000 {
+                               bin := rng.Intn(capacity)
+                               weight := int64(rng.Intn(128))
+                               if step%13 == 0 {
+                                       // Evictions reset a leaf to the 
empty-bin sentinel.
+                                       weight = math.MaxInt64
+                               }
+                               recomputeAllPackingMinimums(&original, bin, 
weight)
+                               optimized.update(bin, weight)
+
+                               if !slices.Equal(original.weights, 
optimized.weights) {
+                                       t.Fatalf("step %d, bin %d: incorrect 
subtree minimums", step, bin)
+                               }
+
+                               start := rng.Intn(capacity)
+                               end := start + 1 + rng.Intn(capacity-start)
+                               limit := int64(rng.Intn(128))
+                               if got, want := optimized.firstFit(start, end, 
limit),
+                                       original.firstFit(start, end, limit); 
got != want {
+                                       t.Fatalf("step %d: firstFit(%d, %d, %d) 
= %d, want %d",
+                                               step, start, end, limit, got, 
want)
+                               }
+                       }
+               })
+       }
+}
+
+var packingIndexUpdateBenchmarkSink int64
+
+func BenchmarkPackingWeightIndexUpdate(b *testing.B) {
+       const period = 8192
+       for _, capacity := range []int{128, 512, 2048} {
+               b.Run(fmt.Sprintf("bins=%d", capacity), func(b *testing.B) {
+                       slots := make([]int, period)
+                       weights := make([]int64, period)
+                       for i := range slots {
+                               slots[i] = (i*73 + 23) % capacity
+                               weights[i] = 64 + int64((i*37)%64)
+                       }
+
+                       for _, optimized := range []bool{false, true} {
+                               name := "before"
+                               if optimized {
+                                       name = "after"
+                               }
+                               b.Run(name, func(b *testing.B) {
+                                       idx := newPackingWeightIndex(capacity)
+                                       for i := range capacity {
+                                               
recomputeAllPackingMinimums(&idx, i, 64+int64(i%64))
+                                       }
+                                       b.ReportAllocs()
+                                       b.ResetTimer()
+                                       if optimized {
+                                               for i := range b.N {
+                                                       j := i & (period - 1)
+                                                       idx.update(slots[j], 
weights[j])
+                                               }
+                                       } else {
+                                               for i := range b.N {
+                                                       j := i & (period - 1)
+                                                       
recomputeAllPackingMinimums(&idx, slots[j], weights[j])
+                                               }
+                                       }
+                                       packingIndexUpdateBenchmarkSink = 
idx.weights[1]
+                               })
+                       }
+               })
+       }
+}

Reply via email to