Author: mduerig
Date: Mon Nov  7 12:35:36 2016
New Revision: 1768507

URL: http://svn.apache.org/viewvc?rev=1768507&view=rev
Log:
OAK-5050: Optimise ImmutableRecordNumbers
Replace map based implementation with an array based one

Removed:
    
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/RecordNumbersIterator.java
Modified:
    
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/ImmutableRecordNumbers.java
    
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/RecordNumbers.java
    
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/Segment.java
    
jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/ImmutableRecordNumbersTest.java

Modified: 
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/ImmutableRecordNumbers.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/ImmutableRecordNumbers.java?rev=1768507&r1=1768506&r2=1768507&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/ImmutableRecordNumbers.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/ImmutableRecordNumbers.java
 Mon Nov  7 12:35:36 2016
@@ -17,10 +17,11 @@
 
 package org.apache.jackrabbit.oak.segment;
 
-import static com.google.common.collect.Maps.newLinkedHashMap;
-
 import java.util.Iterator;
-import java.util.Map;
+
+import javax.annotation.Nonnull;
+
+import com.google.common.collect.AbstractIterator;
 
 /**
  * An immutable record table. It is initialized at construction time and can
@@ -30,32 +31,65 @@ import java.util.Map;
  */
 class ImmutableRecordNumbers implements RecordNumbers {
 
-    private final Map<Integer, RecordEntry> records;
+    @Nonnull
+    private final int[] offsets;
+
+    @Nonnull
+    private final byte[] type;
 
     /**
-     * Create a new immutable record table.
+     * Create a new instance based on arrays for the offsets and types.
+     * <p>
+     * <em>Note:</em> for performance reasons these arrays are directly 
referenced
+     * by this class and must not anymore be modified from other places.
      *
-     * @param records a map of record numbers to record entries. It can't be
-     *                {@code null}.
+     * @param offsets  Offsets per position. -1 if not mapped.
+     * @param type     Types per position. Not defined if not mapped.
      */
-    ImmutableRecordNumbers(Map<Integer, RecordEntry> records) {
-        this.records = newLinkedHashMap(records);
+    public ImmutableRecordNumbers(@Nonnull int[] offsets, @Nonnull byte[] 
type) {
+        this.offsets = offsets;
+        this.type = type;
     }
 
+
     @Override
     public int getOffset(int recordNumber) {
-        RecordEntry entry = records.get(recordNumber);
-
-        if (entry == null) {
+        if (recordNumber < offsets.length) {
+            return offsets[recordNumber];
+        } else {
             return -1;
         }
-
-        return entry.getOffset();
     }
 
     @Override
     public Iterator<Entry> iterator() {
-        return new RecordNumbersIterator(records.entrySet().iterator());
+        return new AbstractIterator<Entry>() {
+            private int pos = -1;
+            @Override
+            protected Entry computeNext() {
+                while (++pos < offsets.length && offsets[pos] < 0) { }
+                if (pos < offsets.length) {
+                    return new Entry() {
+                        @Override
+                        public int getRecordNumber() {
+                            return pos;
+                        }
+
+                        @Override
+                        public int getOffset() {
+                            return offsets[pos];
+                        }
+
+                        @Override
+                        public RecordType getType() {
+                            return RecordType.values()[type[pos]];
+                        }
+                    };
+                } else {
+                    return endOfData();
+                }
+            }
+        };
     }
 
 }

Modified: 
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/RecordNumbers.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/RecordNumbers.java?rev=1768507&r1=1768506&r2=1768507&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/RecordNumbers.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/RecordNumbers.java
 Mon Nov  7 12:35:36 2016
@@ -17,6 +17,10 @@
 
 package org.apache.jackrabbit.oak.segment;
 
+import static com.google.common.collect.Iterators.emptyIterator;
+
+import java.util.Iterator;
+
 import org.apache.jackrabbit.oak.segment.RecordNumbers.Entry;
 
 /**
@@ -25,6 +29,21 @@ import org.apache.jackrabbit.oak.segment
 interface RecordNumbers extends Iterable<Entry> {
 
     /**
+     * An always empty {@code RecordNumber} table.
+     */
+    RecordNumbers EMPTY_RECORD_NUMBERS = new RecordNumbers() {
+        @Override
+        public int getOffset(int recordNumber) {
+            return -1;
+        }
+
+        @Override
+        public Iterator<Entry> iterator() {
+            return emptyIterator();
+        }
+    };
+
+    /**
      * Translate a record number to an offset.
      *
      * @param recordNumber A record number.

Modified: 
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/Segment.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/Segment.java?rev=1768507&r1=1768506&r2=1768507&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/Segment.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/Segment.java
 Mon Nov  7 12:35:36 2016
@@ -22,8 +22,9 @@ import static com.google.common.base.Pre
 import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.base.Preconditions.checkPositionIndexes;
 import static com.google.common.base.Preconditions.checkState;
-import static com.google.common.collect.Maps.newLinkedHashMap;
+import static java.util.Arrays.fill;
 import static org.apache.jackrabbit.oak.commons.IOUtils.closeQuietly;
+import static 
org.apache.jackrabbit.oak.segment.RecordNumbers.EMPTY_RECORD_NUMBERS;
 import static org.apache.jackrabbit.oak.segment.SegmentId.isDataSegmentId;
 import static org.apache.jackrabbit.oak.segment.SegmentVersion.LATEST_VERSION;
 import static org.apache.jackrabbit.oak.segment.SegmentVersion.isValid;
@@ -38,7 +39,6 @@ import java.nio.channels.Channels;
 import java.nio.channels.WritableByteChannel;
 import java.util.Arrays;
 import java.util.Iterator;
-import java.util.Map;
 import java.util.UUID;
 
 import javax.annotation.CheckForNull;
@@ -225,24 +225,29 @@ public class Segment {
      * @return An instance of {@link RecordNumbers}, never {@code null}.
      */
     private RecordNumbers readRecordNumberOffsets() {
-        Map<Integer, RecordEntry> recordNumberOffsets = newLinkedHashMap();
-
-        int position = data.position();
+        int recordNumberCount = getRecordNumberCount();
+        if (recordNumberCount == 0) {
+            return EMPTY_RECORD_NUMBERS;
+        }
 
-        position += HEADER_SIZE;
-        position += getReferencedSegmentIdCount() * SEGMENT_REFERENCE_SIZE;
+        int position = HEADER_SIZE + data.position()
+                + getReferencedSegmentIdCount() * SEGMENT_REFERENCE_SIZE;
+        int maxIndex = data.getInt(position + (recordNumberCount - 1) * 9);
+
+        byte[] types = new byte[maxIndex + 1];
+        int[] offsets = new int[maxIndex + 1];
+        fill(offsets, -1);
 
-        for (int i = 0; i < getRecordNumberCount(); i++) {
+        for (int i = 0; i < recordNumberCount; i++) {
             int recordNumber = data.getInt(position);
             position += 4;
-            int type = data.get(position);
+            types[recordNumber] = data.get(position);
             position += 1;
-            int offset = data.getInt(position);
+            offsets[recordNumber] = data.getInt(position);
             position += 4;
-            recordNumberOffsets.put(recordNumber, new 
RecordEntry(RecordType.values()[type], offset));
         }
 
-        return new ImmutableRecordNumbers(recordNumberOffsets);
+        return new ImmutableRecordNumbers(offsets, types);
     }
 
     private SegmentReferences readReferencedSegments() {

Modified: 
jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/ImmutableRecordNumbersTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/ImmutableRecordNumbersTest.java?rev=1768507&r1=1768506&r2=1768507&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/ImmutableRecordNumbersTest.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/ImmutableRecordNumbersTest.java
 Mon Nov  7 12:35:36 2016
@@ -18,12 +18,13 @@
 package org.apache.jackrabbit.oak.segment;
 
 import static com.google.common.collect.Maps.newHashMap;
+import static java.util.Arrays.fill;
 import static org.junit.Assert.assertEquals;
 
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Set;
 
-import com.google.common.collect.Maps;
 import org.apache.jackrabbit.oak.segment.RecordNumbers.Entry;
 import org.junit.Test;
 
@@ -37,11 +38,13 @@ public class ImmutableRecordNumbersTest
         entries.put(3, 4);
         entries.put(5, 6);
 
-        ImmutableRecordNumbers table = new 
ImmutableRecordNumbers(recordEntries(entries));
+        ImmutableRecordNumbers table = new 
ImmutableRecordNumbers(offsets(entries), types(entries));
 
         assertEquals(2, table.getOffset(1));
         assertEquals(4, table.getOffset(3));
         assertEquals(6, table.getOffset(5));
+        assertEquals(-1, table.getOffset(2));
+        assertEquals(-1, table.getOffset(42));
     }
 
     @Test
@@ -52,7 +55,7 @@ public class ImmutableRecordNumbersTest
         entries.put(3, 4);
         entries.put(5, 6);
 
-        ImmutableRecordNumbers table = new 
ImmutableRecordNumbers(recordEntries(entries));
+        ImmutableRecordNumbers table = new 
ImmutableRecordNumbers(offsets(entries), types(entries));
 
         entries.put(1, 3);
         entries.put(7, 8);
@@ -61,6 +64,8 @@ public class ImmutableRecordNumbersTest
         assertEquals(2, table.getOffset(1));
         assertEquals(4, table.getOffset(3));
         assertEquals(6, table.getOffset(5));
+        assertEquals(-1, table.getOffset(2));
+        assertEquals(-1, table.getOffset(42));
     }
 
     @Test
@@ -71,7 +76,7 @@ public class ImmutableRecordNumbersTest
         entries.put(3, 4);
         entries.put(5, 6);
 
-        ImmutableRecordNumbers table = new 
ImmutableRecordNumbers(recordEntries(entries));
+        ImmutableRecordNumbers table = new 
ImmutableRecordNumbers(offsets(entries), types(entries));
 
         Map<Integer, Integer> iterated = new HashMap<>();
 
@@ -92,4 +97,31 @@ public class ImmutableRecordNumbersTest
         return entries;
     }
 
+    private int[] offsets(Map<Integer, Integer> entries) {
+        int[] offsets = new int[max(entries.keySet()) + 1];
+        fill(offsets, -1);
+
+        for (Map.Entry<Integer, Integer> entry : entries.entrySet()) {
+            offsets[entry.getKey()] = entry.getValue();
+        }
+
+        return offsets;
+    }
+
+    private byte[] types(Map<Integer, Integer> entries) {
+        byte[] types = new byte[max(entries.keySet()) + 1];
+        fill(types, (byte) RecordType.VALUE.ordinal());
+        return types;
+    }
+
+    private int max(Set<Integer> integers) {
+        int max = -1;
+        for (Integer integer : integers) {
+            if (integer > max) {
+                max = integer;
+            }
+        }
+        return max;
+    }
+
 }


Reply via email to