Author: jukka
Date: Sat Mar 15 16:26:05 2014
New Revision: 1577891

URL: http://svn.apache.org/r1577891
Log:
OAK-1526: Lock contention in SegmentIdFactory

Maintain a lazily initialized array of refids
Optimize the SemgentId hash tables

Added:
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentIdTable.java
   (with props)
Modified:
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/Segment.java
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentTracker.java
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentWriter.java

Modified: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/Segment.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/Segment.java?rev=1577891&r1=1577890&r2=1577891&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/Segment.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/Segment.java
 Sat Mar 15 16:26:05 2014
@@ -103,6 +103,12 @@ public class Segment {
     private final ByteBuffer data;
 
     /**
+     * Referenced segment identifiers. Entries are initialized lazily in
+     * {@link #getRefId(int)}. Set to {@code null} for bulk segments.
+     */
+    private final SegmentId[] refids;
+
+    /**
      * String records read from segment. Used to avoid duplicate
      * copies and repeated parsing of the same strings.
      */
@@ -118,6 +124,22 @@ public class Segment {
         this.tracker = checkNotNull(tracker);
         this.id = checkNotNull(id);
         this.data = checkNotNull(data);
+
+        if (id.isDataSegmentId()) {
+            this.refids = new SegmentId[getRefCount()];
+            refids[0] = id;
+        } else {
+            this.refids = null;
+        }
+    }
+
+    Segment(SegmentTracker tracker, byte[] buffer) {
+        this.tracker = checkNotNull(tracker);
+        this.id = tracker.newDataSegmentId();
+        this.data = ByteBuffer.wrap(checkNotNull(buffer));
+
+        this.refids = new SegmentId[SEGMENT_REFERENCE_LIMIT + 1];
+        refids[0] = id;
     }
 
     /**
@@ -137,22 +159,28 @@ public class Segment {
     }
 
     public SegmentId getSegmentId() {
-        return id;
+        return refids[0];
     }
 
     int getRefCount() {
         return (data.get(REF_COUNT_OFFSET) & 0xff) + 1;
     }
 
-    SegmentId getRefId(int refid) {
-        if (refid == 0) {
-            return id;
-        } else {
-            int refpos = data.position() + refid * 16;
-            long msb = data.getLong(refpos);
-            long lsb = data.getLong(refpos + 8);
-            return tracker.getSegmentId(msb, lsb);
+    SegmentId getRefId(int index) {
+        SegmentId refid = refids[index];
+        if (refid == null) {
+            synchronized (this) {
+                refid = refids[index];
+                if (refid == null) {
+                    int refpos = data.position() + index * 16;
+                    long msb = data.getLong(refpos);
+                    long lsb = data.getLong(refpos + 8);
+                    refid = tracker.getSegmentId(msb, lsb);
+                    refids[index] = refid;
+                }
+            }
         }
+        return refid;
     }
 
     public List<SegmentId> getReferencedIds() {
@@ -169,11 +197,14 @@ public class Segment {
     }
 
     public long getCacheSize() {
-        if (data.isDirect()) {
-            return 1024 + data.remaining();
-        } else {
-            return 1024 + 2 * data.remaining();
+        int size = 1024;
+        if (!data.isDirect()) {
+            size += size();
+        }
+        if (id.isDataSegmentId()) {
+            size += size();
         }
+        return size;
     }
 
     /**

Added: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentIdTable.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentIdTable.java?rev=1577891&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentIdTable.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentIdTable.java
 Sat Mar 15 16:26:05 2014
@@ -0,0 +1,140 @@
+/*
+ * 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 org.apache.jackrabbit.oak.plugins.segment;
+
+import static com.google.common.collect.Lists.newArrayList;
+import static com.google.common.collect.Maps.newHashMapWithExpectedSize;
+import static java.util.Collections.nCopies;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * Hash table of weak references to segment identifiers.
+ */
+public class SegmentIdTable {
+
+    /**
+     * Hash table of weak references to segment identifiers that are
+     * currently being accessed. The size of the table is always a power
+     * of two, which optimizes the {@link #expand()} operation. The table is
+     * indexed by the random identifier bits, which guarantees uniform
+     * distribution of entries. Each table entry is either {@code null}
+     * (when there are no matching identifiers) or a list of weak references
+     * to the matching identifiers.
+     */
+    private final ArrayList<WeakReference<SegmentId>> references =
+            newArrayList(nCopies(1024, (WeakReference<SegmentId>) null));
+
+    private final SegmentTracker tracker;
+
+    SegmentIdTable(SegmentTracker tracker) {
+        this.tracker = tracker;
+    }
+
+    /**
+     * 
+     * @param msb
+     * @param lsb
+     * @return
+     */
+    synchronized SegmentId getSegmentId(long msb, long lsb) {
+        int first = getIndex(lsb);
+        int index = first;
+
+        WeakReference<SegmentId> reference = references.get(index);
+        while (reference != null) {
+            SegmentId id = reference.get();
+            if (id != null
+                    && id.getMostSignificantBits() == msb
+                    && id.getLeastSignificantBits() == lsb) {
+                return id;
+            }
+            index = (index + 1) % references.size();
+            reference = references.get(index);
+        }
+
+        SegmentId id = new SegmentId(tracker, msb, lsb);
+        references.set(index, new WeakReference<SegmentId>(id));
+        if (index != first) {
+            refresh();
+        }
+        return id;
+    }
+
+    /**
+     * Returns all segment identifiers that are currently referenced in memory.
+     *
+     * @return referenced segment identifiers
+     */
+    void collectReferencedIds(Collection<SegmentId> ids) {
+        ids.addAll(refresh());
+    }
+
+    private synchronized Collection<SegmentId> refresh() {
+        int size = references.size();
+        Map<SegmentId, WeakReference<SegmentId>> ids =
+                newHashMapWithExpectedSize(size);
+
+        boolean hashCollisions = false;
+        boolean emptyReferences = false;
+        for (int i = 0; i < size; i++) {
+            WeakReference<SegmentId> reference = references.get(i);
+            if (reference != null) {
+                SegmentId id = reference.get();
+                if (id != null) {
+                    ids.put(id, reference);
+                    hashCollisions = hashCollisions || (i != getIndex(id));
+                } else {
+                    references.set(i, null);
+                    emptyReferences = true;
+                }
+            }
+        }
+
+        while (2 * ids.size() > size) {
+            size *= 2;
+        }
+
+        if ((hashCollisions && emptyReferences) || size != references.size()) {
+            references.clear();
+            references.addAll(nCopies(size, (WeakReference<SegmentId>) null));
+
+            for (Map.Entry<SegmentId, WeakReference<SegmentId>> entry
+                    : ids.entrySet()) {
+                int index = getIndex(entry.getKey());
+                while (references.get(index) != null) {
+                    index = (index + 1) % size;
+                }
+                references.set(index, entry.getValue());
+            }
+        }
+
+        return ids.keySet();
+    }
+
+    private int getIndex(SegmentId id) {
+        return getIndex(id.getLeastSignificantBits());
+    }
+
+    private int getIndex(long lsb) {
+        return ((int) lsb) & (references.size() - 1);
+    }
+
+}

Propchange: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentIdTable.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentTracker.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentTracker.java?rev=1577891&r1=1577890&r2=1577891&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentTracker.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentTracker.java
 Sat Mar 15 16:26:05 2014
@@ -16,17 +16,11 @@
  */
 package org.apache.jackrabbit.oak.plugins.segment;
 
-import static com.google.common.collect.Lists.newArrayList;
 import static com.google.common.collect.Lists.newLinkedList;
-import static com.google.common.collect.Sets.newHashSetWithExpectedSize;
+import static com.google.common.collect.Sets.newHashSet;
 
-import java.lang.ref.WeakReference;
 import java.security.SecureRandom;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Iterator;
 import java.util.LinkedList;
-import java.util.List;
 import java.util.Set;
 
 /**
@@ -54,6 +48,12 @@ public class SegmentTracker {
      */
     private final SecureRandom random = new SecureRandom();
 
+    private final SegmentStore store;
+
+    private final SegmentWriter writer;
+
+    private final long cacheSize;
+
     /**
      * Hash table of weak references to segment identifiers that are
      * currently being accessed. The size of the table is always a power
@@ -63,20 +63,17 @@ public class SegmentTracker {
      * (when there are no matching identifiers) or a list of weak references
      * to the matching identifiers.
      */
-    private final ArrayList<List<WeakReference<SegmentId>>> uuids = 
newArrayList(
-            Collections.<List<WeakReference<SegmentId>>>nCopies(1024, null));
+    private final SegmentIdTable[] tables = new SegmentIdTable[32];
 
     private final LinkedList<Segment> segments = newLinkedList();
 
-    private final SegmentStore store;
-
-    private final SegmentWriter writer;
-
-    private final long cacheSize;
-
     private long currentSize = 0;
 
     public SegmentTracker(SegmentStore store, int cacheSizeMB) {
+        for (int i = 0; i < tables.length; i++) {
+            tables[i] = new SegmentIdTable(this);
+        }
+
         this.store = store;
         this.writer = new SegmentWriter(store, this);
         this.cacheSize = cacheSizeMB * MB;
@@ -118,27 +115,11 @@ public class SegmentTracker {
      * @return referenced segment identifiers
      */
     public synchronized Set<SegmentId> getReferencedSegmentIds() {
-        Set<SegmentId> set = newHashSetWithExpectedSize(uuids.size());
-
-        for (int i = 0; i < uuids.size(); i++) {
-            List<WeakReference<SegmentId>> list = uuids.get(i);
-            if (list != null) {
-                Iterator<WeakReference<SegmentId>> iterator = list.iterator();
-                while (iterator.hasNext()) {
-                    SegmentId id = iterator.next().get();
-                    if (id == null) {
-                        iterator.remove();
-                    } else {
-                        set.add(id);
-                    }
-                }
-                if (list.isEmpty()) {
-                    uuids.set(i, null);
-                }
-            }
+        Set<SegmentId> ids = newHashSet();
+        for (SegmentIdTable table : tables) {
+            table.collectReferencedIds(ids);
         }
-
-        return set;
+        return ids;
     }
 
     /**
@@ -147,33 +128,9 @@ public class SegmentTracker {
      * @param lsb
      * @return
      */
-    public synchronized SegmentId getSegmentId(long msb, long lsb) {
-        int index = ((int) lsb) & (uuids.size() - 1);
-
-        List<WeakReference<SegmentId>> list = uuids.get(index);
-        if (list == null) {
-            list = newLinkedList();
-            uuids.set(index, list);
-        }
-
-        Iterator<WeakReference<SegmentId>> iterator = list.iterator();
-        while (iterator.hasNext()) {
-            SegmentId id = iterator.next().get();
-            if (id == null) {
-                iterator.remove();
-            } else if (id.equals(msb, lsb)) {
-                return id;
-            }
-        }
-
-        SegmentId id = new SegmentId(this, msb, lsb);
-        list.add(new WeakReference<SegmentId>(id));
-
-        if (list.size() > 5) {
-            expand();
-        }
-
-        return id;
+    public SegmentId getSegmentId(long msb, long lsb) {
+        int index = ((int) msb) & (tables.length - 1);
+        return tables[index].getSegmentId(msb, lsb);
     }
 
     SegmentId newDataSegmentId() {
@@ -190,39 +147,4 @@ public class SegmentTracker {
         return getSegmentId(msb, lsb);
     }
 
-    private synchronized void expand() {
-        int n = uuids.size();
-        uuids.ensureCapacity(n * 2);
-        for (int i = 0; i < n; i++) {
-            List<WeakReference<SegmentId>> list = uuids.get(i);
-            if (list == null) {
-                uuids.add(null);
-            } else {
-                List<WeakReference<SegmentId>> newList = newLinkedList();
-
-                Iterator<WeakReference<SegmentId>>iterator = list.iterator();
-                while (iterator.hasNext()) {
-                    WeakReference<SegmentId> reference = iterator.next();
-                    SegmentId uuid = reference.get();
-                    if (uuid == null) {
-                        iterator.remove();
-                    } else if ((uuid.getLeastSignificantBits() & n) != 0) {
-                        iterator.remove();
-                        newList.add(reference);
-                    }
-                }
-
-                if (list.isEmpty()) {
-                    uuids.set(i, null);
-                }
-
-                if (newList.isEmpty()) {
-                    uuids.add(null);
-                } else {
-                    uuids.add(newList);
-                }
-            }
-        }
-    }
-
 }

Modified: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentWriter.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentWriter.java?rev=1577891&r1=1577890&r2=1577891&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentWriter.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentWriter.java
 Sat Mar 15 16:26:05 2014
@@ -141,8 +141,7 @@ public class SegmentWriter {
     public SegmentWriter(SegmentStore store, SegmentTracker tracker) {
         this.store = store;
         this.tracker = tracker;
-        this.segment = new Segment(
-                tracker, tracker.newDataSegmentId(), ByteBuffer.wrap(buffer));
+        this.segment = new Segment(tracker, buffer);
         segment.getSegmentId().setSegment(segment);
     }
 
@@ -189,9 +188,7 @@ public class SegmentWriter {
             roots.clear();
             length = 0;
             position = buffer.length;
-            segment = new Segment(
-                    tracker, tracker.newDataSegmentId(),
-                    ByteBuffer.wrap(buffer));
+            segment = new Segment(tracker, buffer);
             segment.getSegmentId().setSegment(segment);
         }
     }


Reply via email to