kinolaev opened a new pull request, #17864:
URL: https://github.com/apache/iceberg/pull/17864

   `CharSequenceMap.computeIfAbsent` computes a hash code for the provided key 
on every call. Because rows in a position delete file are sorted by 
`file_path`, we only need to call `computeIfAbsent` when the key changes.
   
   ```
   Benchmark                                      (numDataFiles)  (numDeletes)  
Mode  Cnt  Score    Error  Units
   ToPositionIndexesBenchmark.computeIfAbsent                  1       1000000  
  ss    5  0.310 ±  0.002   s/op
   ToPositionIndexesBenchmark.computeIfAbsent                 10       1000000  
  ss    5  0.314 ±  0.012   s/op
   ToPositionIndexesBenchmark.computeIfAbsent                100       1000000  
  ss    5  0.311 ±  0.009   s/op
   ToPositionIndexesBenchmark.computeIfAbsent               1000       1000000  
  ss    5  0.133 ±  0.008   s/op
   ToPositionIndexesBenchmark.computeIfAbsent              10000       1000000  
  ss    5  0.316 ±  0.028   s/op
   ToPositionIndexesBenchmark.computeIfAbsent             100000       1000000  
  ss    5  0.196 ±  0.050   s/op
   ToPositionIndexesBenchmark.computeIfAbsent            1000000       1000000  
  ss    5  0.748 ±  0.578   s/op
   ToPositionIndexesBenchmark.stringEquals                     1       1000000  
  ss    5  0.023 ±  0.050   s/op
   ToPositionIndexesBenchmark.stringEquals                    10       1000000  
  ss    5  0.016 ±  0.001   s/op
   ToPositionIndexesBenchmark.stringEquals                   100       1000000  
  ss    5  0.017 ±  0.001   s/op
   ToPositionIndexesBenchmark.stringEquals                  1000       1000000  
  ss    5  0.019 ±  0.011   s/op
   ToPositionIndexesBenchmark.stringEquals                 10000       1000000  
  ss    5  0.027 ±  0.025   s/op
   ToPositionIndexesBenchmark.stringEquals                100000       1000000  
  ss    5  0.068 ±  0.027   s/op
   ToPositionIndexesBenchmark.stringEquals               1000000       1000000  
  ss    5  0.622 ±  0.519   s/op
   ToPositionIndexesBenchmark.comparatorsFilePath              1       1000000  
  ss    5  0.151 ±  0.007   s/op
   ToPositionIndexesBenchmark.comparatorsFilePath             10       1000000  
  ss    5  0.151 ±  0.005   s/op
   ToPositionIndexesBenchmark.comparatorsFilePath            100       1000000  
  ss    5  0.153 ±  0.001   s/op
   ToPositionIndexesBenchmark.comparatorsFilePath           1000       1000000  
  ss    5  0.153 ±  0.004   s/op
   ToPositionIndexesBenchmark.comparatorsFilePath          10000       1000000  
  ss    5  0.277 ±  0.012   s/op
   ToPositionIndexesBenchmark.comparatorsFilePath         100000       1000000  
  ss    5  0.193 ±  0.017   s/op
   ToPositionIndexesBenchmark.comparatorsFilePath        1000000       1000000  
  ss    5  0.612 ±  0.378   s/op
   ```
   
   <details>
   
   <summary>ToPositionIndexesBenchmark.java</summary>
   
   ```java
   package org.apache.iceberg.deletes;
   
   import java.io.IOException;
   import java.io.UncheckedIOException;
   import java.util.List;
   import java.util.Locale;
   import java.util.concurrent.TimeUnit;
   import org.apache.iceberg.Accessor;
   import org.apache.iceberg.DeleteFile;
   import org.apache.iceberg.MetadataColumns;
   import org.apache.iceberg.Schema;
   import org.apache.iceberg.StructLike;
   import org.apache.iceberg.io.CloseableIterable;
   import org.apache.iceberg.io.DeleteSchemaUtil;
   import org.apache.iceberg.relocated.com.google.common.collect.Lists;
   import org.apache.iceberg.types.Comparators;
   import org.apache.iceberg.util.CharSequenceMap;
   import org.openjdk.jmh.annotations.Benchmark;
   import org.openjdk.jmh.annotations.BenchmarkMode;
   import org.openjdk.jmh.annotations.Fork;
   import org.openjdk.jmh.annotations.Measurement;
   import org.openjdk.jmh.annotations.Mode;
   import org.openjdk.jmh.annotations.Param;
   import org.openjdk.jmh.annotations.Scope;
   import org.openjdk.jmh.annotations.Setup;
   import org.openjdk.jmh.annotations.State;
   import org.openjdk.jmh.annotations.Threads;
   import org.openjdk.jmh.annotations.Timeout;
   import org.openjdk.jmh.annotations.Warmup;
   import org.openjdk.jmh.infra.Blackhole;
   
   @Fork(1)
   @State(Scope.Benchmark)
   @Warmup(iterations = 3)
   @Measurement(iterations = 5)
   @BenchmarkMode(Mode.SingleShotTime)
   @Timeout(time = 10, timeUnit = TimeUnit.MINUTES)
   public class ToPositionIndexesBenchmark {
     private static final Schema POS_DELETE_SCHEMA = 
DeleteSchemaUtil.pathPosSchema();
     private static final Accessor<StructLike> FILENAME_ACCESSOR =
         
POS_DELETE_SCHEMA.accessorForField(MetadataColumns.DELETE_FILE_PATH.fieldId());
     private static final Accessor<StructLike> POSITION_ACCESSOR =
         
POS_DELETE_SCHEMA.accessorForField(MetadataColumns.DELETE_FILE_POS.fieldId());
   
     @Param("1000000")
     private int numDeletes;
   
     @Param({"1", "10", "100", "1000", "10000", "100000", "1000000"})
     private int numDataFiles;
   
     private List<StructLike> deletes;
     private String pathTemplate =
         
"s3://pretty-long-bucket-name/pretty-long-namespace-name/pretty-long-table-name/data/%07d-data.parquet";
   
     @Setup
     public void setupBenchmark() {
       deletes = Lists.newArrayListWithExpectedSize(numDeletes);
       int numDeletesPerDataFile = numDeletes / numDataFiles;
       for (int index = 0; index < numDeletes; index++) {
         String path = String.format(Locale.ROOT, pathTemplate, index / 
numDeletesPerDataFile);
         deletes.add(new PositionDeleteRow(path, index % 
numDeletesPerDataFile));
       }
     }
   
     @Benchmark
     @Threads(1)
     public void computeIfAbsent(Blackhole blackhole) {
       
blackhole.consume(toPositionIndexesComputeIfAbsent(CloseableIterable.withNoopClose(deletes),
 null));
     }
   
     // 
https://github.com/apache/iceberg/blob/apache-iceberg-1.11.0/core/src/main/java/org/apache/iceberg/deletes/Deletes.java#L139
     private static <T extends StructLike> CharSequenceMap<PositionDeleteIndex> 
toPositionIndexesComputeIfAbsent(
         CloseableIterable<T> posDeletes, DeleteFile file) {
       CharSequenceMap<PositionDeleteIndex> indexes = CharSequenceMap.create();
   
       try (CloseableIterable<T> deletes = posDeletes) {
         for (T delete : deletes) {
           CharSequence filePath = (CharSequence) FILENAME_ACCESSOR.get(delete);
           long position = (long) POSITION_ACCESSOR.get(delete);
           PositionDeleteIndex index =
               indexes.computeIfAbsent(filePath, key -> new 
BitmapPositionDeleteIndex(file));
           index.delete(position);
         }
       } catch (IOException e) {
         throw new UncheckedIOException("Failed to close position delete 
source", e);
       }
   
       return indexes;
     }
     @Benchmark
     @Threads(1)
     public void comparatorsFilePath(Blackhole blackhole) {
       blackhole.consume(
           
toPositionIndexesComparatorsFilePath(CloseableIterable.withNoopClose(deletes), 
null));
     }
   
     public static <T extends StructLike>
         CharSequenceMap<PositionDeleteIndex> 
toPositionIndexesComparatorsFilePath(
             CloseableIterable<T> posDeletes, DeleteFile file) {
       CharSequenceMap<PositionDeleteIndex> indexes = CharSequenceMap.create();
   
       try (CloseableIterable<T> deletes = posDeletes) {
         CharSequence lastFilePath = null;
         PositionDeleteIndex index = null;
         for (T delete : deletes) {
           CharSequence filePath = (CharSequence) FILENAME_ACCESSOR.get(delete);
           long position = (long) POSITION_ACCESSOR.get(delete);
           if (lastFilePath == null || 
Comparators.filePath().compare(lastFilePath, filePath) != 0) {
             lastFilePath = filePath;
             index = indexes.computeIfAbsent(filePath, key -> new 
BitmapPositionDeleteIndex(file));
           }
           index.delete(position);
         }
       } catch (IOException e) {
         throw new UncheckedIOException("Failed to close position delete 
source", e);
       }
   
       return indexes;
     }
   
     @Benchmark
     @Threads(1)
     public void stringEquals(Blackhole blackhole) {
       blackhole.consume(
           
toPositionIndexesStringEquals(CloseableIterable.withNoopClose(deletes), null));
     }
   
     public static <T extends StructLike>
         CharSequenceMap<PositionDeleteIndex> toPositionIndexesStringEquals(
             CloseableIterable<T> posDeletes, DeleteFile file) {
       CharSequenceMap<PositionDeleteIndex> indexes = CharSequenceMap.create();
   
       try (CloseableIterable<T> deletes = posDeletes) {
         String lastFilePath = null;
         PositionDeleteIndex index = null;
         for (T delete : deletes) {
           CharSequence filePath = ((CharSequence) 
FILENAME_ACCESSOR.get(delete));
           long position = (long) POSITION_ACCESSOR.get(delete);
           if (lastFilePath == null || !lastFilePath.equals(filePath)) {
             lastFilePath = filePath.toString();
             index = indexes.computeIfAbsent(filePath, key -> new 
BitmapPositionDeleteIndex(file));
           }
           index.delete(position);
         }
       } catch (IOException e) {
         throw new UncheckedIOException("Failed to close position delete 
source", e);
       }
   
       return indexes;
     }
   
     private record PositionDeleteRow(String path, long position) implements 
StructLike {
       @Override
       public int size() {
         return 2;
       }
   
       @Override
       @SuppressWarnings("unchecked")
       public <T> T get(int pos, Class<T> javaClass) {
         switch (pos) {
           case 0:
             return (T) path;
           case 1:
             return (T) (Long) position;
           default:
             throw new UnsupportedOperationException("Unsupported position: " + 
pos);
         }
       }
   
       @Override
       public <T> void set(int pos, T value) {
         throw new UnsupportedOperationException("Not supported");
       }
     }
   }
   ```
   </details>
   
   Related to #11648.
   Extracted from #15714.
   
   @anuragmantri


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to