You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by jp...@apache.org on 2012/06/29 14:52:55 UTC

svn commit: r1355346 - in /lucene/dev/trunk/lucene: CHANGES.txt core/src/java/org/apache/lucene/util/packed/Packed64.java

Author: jpountz
Date: Fri Jun 29 12:52:54 2012
New Revision: 1355346

URL: http://svn.apache.org/viewvc?rev=1355346&view=rev
Log:
LUCENE-4171: Performance improvements to Packed64 (Toke Eskildsen).

Modified:
    lucene/dev/trunk/lucene/CHANGES.txt
    lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/util/packed/Packed64.java

Modified: lucene/dev/trunk/lucene/CHANGES.txt
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/CHANGES.txt?rev=1355346&r1=1355345&r2=1355346&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/CHANGES.txt (original)
+++ lucene/dev/trunk/lucene/CHANGES.txt Fri Jun 29 12:52:54 2012
@@ -1036,6 +1036,9 @@ Optimizations
   the cloned instances. WeakIdentityMap was extended to support
   iterating over its keys.  (Uwe Schindler)
 
+* LUCENE-4171: Performance improvements to Packed64.
+  (Toke Eskildsen via Adrien Grand)
+
 Bug fixes
 
 * LUCENE-2803: The FieldCache can miss values if an entry for a reader

Modified: lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/util/packed/Packed64.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/util/packed/Packed64.java?rev=1355346&r1=1355345&r2=1355346&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/util/packed/Packed64.java (original)
+++ lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/util/packed/Packed64.java Fri Jun 29 12:52:54 2012
@@ -25,99 +25,40 @@ import java.util.Arrays;
 
 /**
  * Space optimized random access capable array of values with a fixed number of
- * bits. For 32 bits/value and less, performance on 32 bit machines is not
- * optimal. Consider using {@link Packed32} for such a setup.
+ * bits/value. Values are packed contiguously.
  * </p><p>
- * The implementation strives to avoid conditionals and expensive operations,
- * sacrificing code clarity to achieve better performance.
+ * The implementation strives to perform af fast as possible under the
+ * constraint of contiguous bits, by avoiding expensive operations. This comes
+ * at the cost of code clarity.
+ * </p><p>
+ * Technical details: This implementation is a refinement of a non-branching
+ * version. The non-branching get and set methods meant that 2 or 4 atomics in
+ * the underlying array were always accessed, even for the cases where only
+ * 1 or 2 were needed. Even with caching, this had a detrimental effect on
+ * performance.
+ * Related to this issue, the old implementation used lookup tables for shifts
+ * and masks, which also proved to be a bit slower than calculating the shifts
+ * and masks on the fly.
+ * See https://issues.apache.org/jira/browse/LUCENE-4062 for details.
+ *
  */
-
 class Packed64 extends PackedInts.MutableImpl {
   static final int BLOCK_SIZE = 64; // 32 = int, 64 = long
   static final int BLOCK_BITS = 6; // The #bits representing BLOCK_SIZE
   static final int MOD_MASK = BLOCK_SIZE - 1; // x % BLOCK_SIZE
 
-  private static final int ENTRY_SIZE = BLOCK_SIZE + 1;
-  static final int FAC_BITPOS = 3;
-
-  /*
-   * In order to make an efficient value-getter, conditionals should be
-   * avoided. A value can be positioned inside of a block, requiring shifting
-   * left or right or it can span two blocks, requiring a left-shift on the
-   * first block and a right-shift on the right block.
-   * </p><p>
-   * By always shifting the first block both left and right, we get exactly
-   * the right bits. By always shifting the second block right and applying
-   * a mask, we get the right bits there. After that, we | the two bitsets.
-  */
-  static final int[][] SHIFTS =
-    new int[ENTRY_SIZE][ENTRY_SIZE * FAC_BITPOS];
-  static final long[][] MASKS = new long[ENTRY_SIZE][ENTRY_SIZE];
-
-  static { // Generate shifts
-      for (int elementBits = 1 ; elementBits <= BLOCK_SIZE ; elementBits++) {
-          for (int bitPos = 0 ; bitPos < BLOCK_SIZE ; bitPos++) {
-              int[] currentShifts = SHIFTS[elementBits];
-              int base = bitPos * FAC_BITPOS;
-              currentShifts[base    ] = bitPos;
-              currentShifts[base + 1] = BLOCK_SIZE - elementBits;
-              if (bitPos <= BLOCK_SIZE - elementBits) { // Single block
-                  currentShifts[base + 2] = 0;
-                  MASKS[elementBits][bitPos] = 0;
-              } else { // Two blocks
-                  int rBits = elementBits - (BLOCK_SIZE - bitPos);
-                  currentShifts[base + 2] = BLOCK_SIZE - rBits;
-                  MASKS[elementBits][bitPos] = ~(~0L << rBits);
-              }
-          }
-      }
-  }
-
-  /*
-   * The setter requires more masking than the getter.
-  */
-  private static final long[][] WRITE_MASKS =
-          new long[ENTRY_SIZE][ENTRY_SIZE * FAC_BITPOS];
-  static {
-      for (int elementBits = 1 ; elementBits <= BLOCK_SIZE ; elementBits++) {
-          long elementPosMask = ~(~0L << elementBits);
-          int[] currentShifts = SHIFTS[elementBits];
-          long[] currentMasks = WRITE_MASKS[elementBits];
-          for (int bitPos = 0 ; bitPos < BLOCK_SIZE ; bitPos++) {
-              int base = bitPos * FAC_BITPOS;
-              currentMasks[base  ] =~((elementPosMask
-                                 << currentShifts[base + 1])
-                                >>> currentShifts[base]);
-              if (bitPos <= BLOCK_SIZE - elementBits) { // Second block not used
-                currentMasks[base+1] = ~0; // Keep all bits
-                currentMasks[base+2] = 0;  // Or with 0
-              } else {
-                currentMasks[base+1] = ~(elementPosMask
-                                         << currentShifts[base + 2]);
-                currentMasks[base+2] = currentShifts[base + 2] == 0 ? 0 : ~0;
-              }
-          }
-      }
-  }
-
-  private static int pgcd(int a, int b) {
-    if (a < b) {
-      return pgcd(b, a);
-    } else if (b == 0) {
-      return a;
-    } else {
-      return pgcd(b, a % b);
-    }
-  }
-
-  /* The bits */
+  /**
+   * Values are stores contiguously in the blocks array.
+   */
   private final long[] blocks;
-
-  // Cached calculations
-  private int maxPos;      // blocks.length * BLOCK_SIZE / elementBits - 1
-  private int[] shifts;    // The shifts for the current elementBits
-  private long[] readMasks;
-  private long[] writeMasks;
+  /**
+   * A right-aligned mask of width BitsPerValue used by {@link #get(int)}.
+   */
+  private final long maskRight;
+  /**
+   * Optimization: Saves one lookup in {@link #get(int)}.
+   */
+  private final int bpvMinusBlockSize;
 
   /**
    * Creates an array with the internal structures adjusted for the given
@@ -126,18 +67,18 @@ class Packed64 extends PackedInts.Mutabl
    * @param bitsPerValue the number of bits available for any given value.
    */
   public Packed64(int valueCount, int bitsPerValue) {
-    // TODO: Test for edge-cases (2^31 values, 63 bitsPerValue)
-    // +2 due to the avoid-conditionals-trick. The last entry is always 0
-    this(new long[(int)((long)valueCount * bitsPerValue / BLOCK_SIZE + 2)],
+    // NOTE: block-size was previously calculated as
+    // valueCount * bitsPerValue / BLOCK_SIZE + 1
+    // due to memory layout requirements dictated by non-branching code
+    this(new long[size(valueCount, bitsPerValue)],
             valueCount, bitsPerValue);
   }
 
-
   /**
    * Creates an array backed by the given blocks.
    * </p><p>
    * Note: The blocks are used directly, so changes to the given block will
-   * affect the Packed32-structure.
+   * affect the Packed64-structure.
    * @param blocks   used as the internal backing array. Not that the last
    *                 element cannot be addressed directly.
    * @param valueCount the number of values.
@@ -146,7 +87,8 @@ class Packed64 extends PackedInts.Mutabl
   public Packed64(long[] blocks, int valueCount, int bitsPerValue) {
     super(valueCount, bitsPerValue);
     this.blocks = blocks;
-    updateCached();
+    maskRight = ~0L << (BLOCK_SIZE-bitsPerValue) >>> (BLOCK_SIZE-bitsPerValue);
+    bpvMinusBlockSize = bitsPerValue - BLOCK_SIZE;
   }
 
   /**
@@ -161,12 +103,12 @@ class Packed64 extends PackedInts.Mutabl
                                                             throws IOException {
     super(valueCount, bitsPerValue);
     int size = size(valueCount, bitsPerValue);
-    blocks = new long[size+1]; // +1 due to non-conditional tricks
-    // TODO: find a faster way to bulk-read longs...
+    blocks = new long[size]; // Previously +1 due to non-conditional tricks
     for(int i=0;i<size;i++) {
       blocks[i] = in.readLong();
     }
-    updateCached();
+    maskRight = ~0L << (BLOCK_SIZE-bitsPerValue) >>> (BLOCK_SIZE-bitsPerValue);
+    bpvMinusBlockSize = bitsPerValue - BLOCK_SIZE;
   }
 
   private static int size(int valueCount, int bitsPerValue) {
@@ -174,48 +116,57 @@ class Packed64 extends PackedInts.Mutabl
     return (int)(totBitCount/64 + ((totBitCount % 64 == 0 ) ? 0:1));
   }
 
-  private void updateCached() {
-    readMasks = MASKS[bitsPerValue];
-    shifts = SHIFTS[bitsPerValue];
-    writeMasks = WRITE_MASKS[bitsPerValue];
-    maxPos = (int)((((long)blocks.length) * BLOCK_SIZE / bitsPerValue) - 2);
-  }
-
   /**
    * @param index the position of the value.
    * @return the value at the given index.
    */
+  @Override
   public long get(final int index) {
-    assert index >= 0 && index < size();
+    // The abstract index in a bit stream
     final long majorBitPos = (long)index * bitsPerValue;
-    final int elementPos = (int)(majorBitPos >>> BLOCK_BITS); // / BLOCK_SIZE
-    final int bitPos =     (int)(majorBitPos & MOD_MASK); // % BLOCK_SIZE);
-
-    final int base = bitPos * FAC_BITPOS;
-    assert elementPos < blocks.length : "elementPos: " + elementPos + "; blocks.len: " + blocks.length;
-    return ((blocks[elementPos] << shifts[base]) >>> shifts[base+1]) |
-            ((blocks[elementPos+1] >>> shifts[base+2]) & readMasks[bitPos]);
+    // The index in the backing long-array
+    final int elementPos = (int)(majorBitPos >>> BLOCK_BITS);
+    // The number of value-bits in the second long
+    final long endBits = (majorBitPos & MOD_MASK) + bpvMinusBlockSize;
+
+    if (endBits <= 0) { // Single block
+      return (blocks[elementPos] >>> -endBits) & maskRight;
+    }
+    // Two blocks
+    return ((blocks[elementPos] << endBits)
+        | (blocks[elementPos+1] >>> (BLOCK_SIZE - endBits)))
+        & maskRight;
   }
 
+  @Override
   public void set(final int index, final long value) {
+    // The abstract index in a contiguous bit stream
     final long majorBitPos = (long)index * bitsPerValue;
+    // The index in the backing long-array
     final int elementPos = (int)(majorBitPos >>> BLOCK_BITS); // / BLOCK_SIZE
-    final int bitPos =     (int)(majorBitPos & MOD_MASK); // % BLOCK_SIZE);
-    final int base = bitPos * FAC_BITPOS;
+    // The number of value-bits in the second long
+    final long endBits = (majorBitPos & MOD_MASK) + bpvMinusBlockSize;
 
-    blocks[elementPos  ] = (blocks[elementPos  ] & writeMasks[base])
-                           | (value << shifts[base + 1] >>> shifts[base]);
-    blocks[elementPos+1] = (blocks[elementPos+1] & writeMasks[base+1])
-                           | ((value << shifts[base + 2]) & writeMasks[base+2]);
+    if (endBits <= 0) { // Single block
+      blocks[elementPos] = blocks[elementPos] &  ~(maskRight << -endBits)
+         | (value << -endBits);
+      return;
+    }
+    // Two blocks
+    blocks[elementPos] = blocks[elementPos] &  ~(maskRight >>> endBits)
+        | (value >>> endBits);
+    blocks[elementPos+1] = blocks[elementPos+1] &  (~0L >>> endBits)
+        | (value << (BLOCK_SIZE - endBits));
   }
 
+
   @Override
   public String toString() {
     return "Packed64(bitsPerValue=" + bitsPerValue + ", size="
-            + size() + ", maxPos=" + maxPos
-            + ", elements.length=" + blocks.length + ")";
+            + size() + ", elements.length=" + blocks.length + ")";
   }
 
+  @Override
   public long ramBytesUsed() {
     return RamUsageEstimator.sizeOf(blocks);
   }
@@ -226,7 +177,7 @@ class Packed64 extends PackedInts.Mutabl
     assert fromIndex <= toIndex;
 
     // minimum number of values that use an exact number of full blocks
-    final int nAlignedValues = 64 / pgcd(64, bitsPerValue);
+    final int nAlignedValues = 64 / gcd(64, bitsPerValue);
     final int span = toIndex - fromIndex;
     if (span <= 3 * nAlignedValues) {
       // there needs be at least 2 * nAlignedValues aligned values for the
@@ -270,6 +221,17 @@ class Packed64 extends PackedInts.Mutabl
     }
   }
 
+  private static int gcd(int a, int b) {
+    if (a < b) {
+      return gcd(b, a);
+    } else if (b == 0) {
+      return a;
+    } else {
+      return gcd(b, a % b);
+    }
+  }
+
+  @Override
   public void clear() {
     Arrays.fill(blocks, 0L);
   }



Re: svn commit: r1355346 - in /lucene/dev/trunk/lucene: CHANGES.txt core/src/java/org/apache/lucene/util/packed/Packed64.java

Posted by Adrien Grand <jp...@gmail.com>.
On Fri, Jun 29, 2012 at 2:55 PM, Robert Muir <rc...@gmail.com> wrote:
> Can we move this CHANGES entry under the beta section?

Fixed. Thanks, Robert.

-- 
Adrien

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@lucene.apache.org
For additional commands, e-mail: dev-help@lucene.apache.org


Re: svn commit: r1355346 - in /lucene/dev/trunk/lucene: CHANGES.txt core/src/java/org/apache/lucene/util/packed/Packed64.java

Posted by Robert Muir <rc...@gmail.com>.
Can we move this CHANGES entry under the beta section?

On Fri, Jun 29, 2012 at 8:52 AM,  <jp...@apache.org> wrote:
> Author: jpountz
> Date: Fri Jun 29 12:52:54 2012
> New Revision: 1355346
>
> URL: http://svn.apache.org/viewvc?rev=1355346&view=rev
> Log:
> LUCENE-4171: Performance improvements to Packed64 (Toke Eskildsen).
>
> Modified:
>    lucene/dev/trunk/lucene/CHANGES.txt
>    lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/util/packed/Packed64.java
>
> Modified: lucene/dev/trunk/lucene/CHANGES.txt
> URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/CHANGES.txt?rev=1355346&r1=1355345&r2=1355346&view=diff
> ==============================================================================
> --- lucene/dev/trunk/lucene/CHANGES.txt (original)
> +++ lucene/dev/trunk/lucene/CHANGES.txt Fri Jun 29 12:52:54 2012
> @@ -1036,6 +1036,9 @@ Optimizations
>   the cloned instances. WeakIdentityMap was extended to support
>   iterating over its keys.  (Uwe Schindler)
>
> +* LUCENE-4171: Performance improvements to Packed64.
> +  (Toke Eskildsen via Adrien Grand)
> +
>  Bug fixes
>
>  * LUCENE-2803: The FieldCache can miss values if an entry for a reader
>
> Modified: lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/util/packed/Packed64.java
> URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/util/packed/Packed64.java?rev=1355346&r1=1355345&r2=1355346&view=diff
> ==============================================================================
> --- lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/util/packed/Packed64.java (original)
> +++ lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/util/packed/Packed64.java Fri Jun 29 12:52:54 2012
> @@ -25,99 +25,40 @@ import java.util.Arrays;
>
>  /**
>  * Space optimized random access capable array of values with a fixed number of
> - * bits. For 32 bits/value and less, performance on 32 bit machines is not
> - * optimal. Consider using {@link Packed32} for such a setup.
> + * bits/value. Values are packed contiguously.
>  * </p><p>
> - * The implementation strives to avoid conditionals and expensive operations,
> - * sacrificing code clarity to achieve better performance.
> + * The implementation strives to perform af fast as possible under the
> + * constraint of contiguous bits, by avoiding expensive operations. This comes
> + * at the cost of code clarity.
> + * </p><p>
> + * Technical details: This implementation is a refinement of a non-branching
> + * version. The non-branching get and set methods meant that 2 or 4 atomics in
> + * the underlying array were always accessed, even for the cases where only
> + * 1 or 2 were needed. Even with caching, this had a detrimental effect on
> + * performance.
> + * Related to this issue, the old implementation used lookup tables for shifts
> + * and masks, which also proved to be a bit slower than calculating the shifts
> + * and masks on the fly.
> + * See https://issues.apache.org/jira/browse/LUCENE-4062 for details.
> + *
>  */
> -
>  class Packed64 extends PackedInts.MutableImpl {
>   static final int BLOCK_SIZE = 64; // 32 = int, 64 = long
>   static final int BLOCK_BITS = 6; // The #bits representing BLOCK_SIZE
>   static final int MOD_MASK = BLOCK_SIZE - 1; // x % BLOCK_SIZE
>
> -  private static final int ENTRY_SIZE = BLOCK_SIZE + 1;
> -  static final int FAC_BITPOS = 3;
> -
> -  /*
> -   * In order to make an efficient value-getter, conditionals should be
> -   * avoided. A value can be positioned inside of a block, requiring shifting
> -   * left or right or it can span two blocks, requiring a left-shift on the
> -   * first block and a right-shift on the right block.
> -   * </p><p>
> -   * By always shifting the first block both left and right, we get exactly
> -   * the right bits. By always shifting the second block right and applying
> -   * a mask, we get the right bits there. After that, we | the two bitsets.
> -  */
> -  static final int[][] SHIFTS =
> -    new int[ENTRY_SIZE][ENTRY_SIZE * FAC_BITPOS];
> -  static final long[][] MASKS = new long[ENTRY_SIZE][ENTRY_SIZE];
> -
> -  static { // Generate shifts
> -      for (int elementBits = 1 ; elementBits <= BLOCK_SIZE ; elementBits++) {
> -          for (int bitPos = 0 ; bitPos < BLOCK_SIZE ; bitPos++) {
> -              int[] currentShifts = SHIFTS[elementBits];
> -              int base = bitPos * FAC_BITPOS;
> -              currentShifts[base    ] = bitPos;
> -              currentShifts[base + 1] = BLOCK_SIZE - elementBits;
> -              if (bitPos <= BLOCK_SIZE - elementBits) { // Single block
> -                  currentShifts[base + 2] = 0;
> -                  MASKS[elementBits][bitPos] = 0;
> -              } else { // Two blocks
> -                  int rBits = elementBits - (BLOCK_SIZE - bitPos);
> -                  currentShifts[base + 2] = BLOCK_SIZE - rBits;
> -                  MASKS[elementBits][bitPos] = ~(~0L << rBits);
> -              }
> -          }
> -      }
> -  }
> -
> -  /*
> -   * The setter requires more masking than the getter.
> -  */
> -  private static final long[][] WRITE_MASKS =
> -          new long[ENTRY_SIZE][ENTRY_SIZE * FAC_BITPOS];
> -  static {
> -      for (int elementBits = 1 ; elementBits <= BLOCK_SIZE ; elementBits++) {
> -          long elementPosMask = ~(~0L << elementBits);
> -          int[] currentShifts = SHIFTS[elementBits];
> -          long[] currentMasks = WRITE_MASKS[elementBits];
> -          for (int bitPos = 0 ; bitPos < BLOCK_SIZE ; bitPos++) {
> -              int base = bitPos * FAC_BITPOS;
> -              currentMasks[base  ] =~((elementPosMask
> -                                 << currentShifts[base + 1])
> -                                >>> currentShifts[base]);
> -              if (bitPos <= BLOCK_SIZE - elementBits) { // Second block not used
> -                currentMasks[base+1] = ~0; // Keep all bits
> -                currentMasks[base+2] = 0;  // Or with 0
> -              } else {
> -                currentMasks[base+1] = ~(elementPosMask
> -                                         << currentShifts[base + 2]);
> -                currentMasks[base+2] = currentShifts[base + 2] == 0 ? 0 : ~0;
> -              }
> -          }
> -      }
> -  }
> -
> -  private static int pgcd(int a, int b) {
> -    if (a < b) {
> -      return pgcd(b, a);
> -    } else if (b == 0) {
> -      return a;
> -    } else {
> -      return pgcd(b, a % b);
> -    }
> -  }
> -
> -  /* The bits */
> +  /**
> +   * Values are stores contiguously in the blocks array.
> +   */
>   private final long[] blocks;
> -
> -  // Cached calculations
> -  private int maxPos;      // blocks.length * BLOCK_SIZE / elementBits - 1
> -  private int[] shifts;    // The shifts for the current elementBits
> -  private long[] readMasks;
> -  private long[] writeMasks;
> +  /**
> +   * A right-aligned mask of width BitsPerValue used by {@link #get(int)}.
> +   */
> +  private final long maskRight;
> +  /**
> +   * Optimization: Saves one lookup in {@link #get(int)}.
> +   */
> +  private final int bpvMinusBlockSize;
>
>   /**
>    * Creates an array with the internal structures adjusted for the given
> @@ -126,18 +67,18 @@ class Packed64 extends PackedInts.Mutabl
>    * @param bitsPerValue the number of bits available for any given value.
>    */
>   public Packed64(int valueCount, int bitsPerValue) {
> -    // TODO: Test for edge-cases (2^31 values, 63 bitsPerValue)
> -    // +2 due to the avoid-conditionals-trick. The last entry is always 0
> -    this(new long[(int)((long)valueCount * bitsPerValue / BLOCK_SIZE + 2)],
> +    // NOTE: block-size was previously calculated as
> +    // valueCount * bitsPerValue / BLOCK_SIZE + 1
> +    // due to memory layout requirements dictated by non-branching code
> +    this(new long[size(valueCount, bitsPerValue)],
>             valueCount, bitsPerValue);
>   }
>
> -
>   /**
>    * Creates an array backed by the given blocks.
>    * </p><p>
>    * Note: The blocks are used directly, so changes to the given block will
> -   * affect the Packed32-structure.
> +   * affect the Packed64-structure.
>    * @param blocks   used as the internal backing array. Not that the last
>    *                 element cannot be addressed directly.
>    * @param valueCount the number of values.
> @@ -146,7 +87,8 @@ class Packed64 extends PackedInts.Mutabl
>   public Packed64(long[] blocks, int valueCount, int bitsPerValue) {
>     super(valueCount, bitsPerValue);
>     this.blocks = blocks;
> -    updateCached();
> +    maskRight = ~0L << (BLOCK_SIZE-bitsPerValue) >>> (BLOCK_SIZE-bitsPerValue);
> +    bpvMinusBlockSize = bitsPerValue - BLOCK_SIZE;
>   }
>
>   /**
> @@ -161,12 +103,12 @@ class Packed64 extends PackedInts.Mutabl
>                                                             throws IOException {
>     super(valueCount, bitsPerValue);
>     int size = size(valueCount, bitsPerValue);
> -    blocks = new long[size+1]; // +1 due to non-conditional tricks
> -    // TODO: find a faster way to bulk-read longs...
> +    blocks = new long[size]; // Previously +1 due to non-conditional tricks
>     for(int i=0;i<size;i++) {
>       blocks[i] = in.readLong();
>     }
> -    updateCached();
> +    maskRight = ~0L << (BLOCK_SIZE-bitsPerValue) >>> (BLOCK_SIZE-bitsPerValue);
> +    bpvMinusBlockSize = bitsPerValue - BLOCK_SIZE;
>   }
>
>   private static int size(int valueCount, int bitsPerValue) {
> @@ -174,48 +116,57 @@ class Packed64 extends PackedInts.Mutabl
>     return (int)(totBitCount/64 + ((totBitCount % 64 == 0 ) ? 0:1));
>   }
>
> -  private void updateCached() {
> -    readMasks = MASKS[bitsPerValue];
> -    shifts = SHIFTS[bitsPerValue];
> -    writeMasks = WRITE_MASKS[bitsPerValue];
> -    maxPos = (int)((((long)blocks.length) * BLOCK_SIZE / bitsPerValue) - 2);
> -  }
> -
>   /**
>    * @param index the position of the value.
>    * @return the value at the given index.
>    */
> +  @Override
>   public long get(final int index) {
> -    assert index >= 0 && index < size();
> +    // The abstract index in a bit stream
>     final long majorBitPos = (long)index * bitsPerValue;
> -    final int elementPos = (int)(majorBitPos >>> BLOCK_BITS); // / BLOCK_SIZE
> -    final int bitPos =     (int)(majorBitPos & MOD_MASK); // % BLOCK_SIZE);
> -
> -    final int base = bitPos * FAC_BITPOS;
> -    assert elementPos < blocks.length : "elementPos: " + elementPos + "; blocks.len: " + blocks.length;
> -    return ((blocks[elementPos] << shifts[base]) >>> shifts[base+1]) |
> -            ((blocks[elementPos+1] >>> shifts[base+2]) & readMasks[bitPos]);
> +    // The index in the backing long-array
> +    final int elementPos = (int)(majorBitPos >>> BLOCK_BITS);
> +    // The number of value-bits in the second long
> +    final long endBits = (majorBitPos & MOD_MASK) + bpvMinusBlockSize;
> +
> +    if (endBits <= 0) { // Single block
> +      return (blocks[elementPos] >>> -endBits) & maskRight;
> +    }
> +    // Two blocks
> +    return ((blocks[elementPos] << endBits)
> +        | (blocks[elementPos+1] >>> (BLOCK_SIZE - endBits)))
> +        & maskRight;
>   }
>
> +  @Override
>   public void set(final int index, final long value) {
> +    // The abstract index in a contiguous bit stream
>     final long majorBitPos = (long)index * bitsPerValue;
> +    // The index in the backing long-array
>     final int elementPos = (int)(majorBitPos >>> BLOCK_BITS); // / BLOCK_SIZE
> -    final int bitPos =     (int)(majorBitPos & MOD_MASK); // % BLOCK_SIZE);
> -    final int base = bitPos * FAC_BITPOS;
> +    // The number of value-bits in the second long
> +    final long endBits = (majorBitPos & MOD_MASK) + bpvMinusBlockSize;
>
> -    blocks[elementPos  ] = (blocks[elementPos  ] & writeMasks[base])
> -                           | (value << shifts[base + 1] >>> shifts[base]);
> -    blocks[elementPos+1] = (blocks[elementPos+1] & writeMasks[base+1])
> -                           | ((value << shifts[base + 2]) & writeMasks[base+2]);
> +    if (endBits <= 0) { // Single block
> +      blocks[elementPos] = blocks[elementPos] &  ~(maskRight << -endBits)
> +         | (value << -endBits);
> +      return;
> +    }
> +    // Two blocks
> +    blocks[elementPos] = blocks[elementPos] &  ~(maskRight >>> endBits)
> +        | (value >>> endBits);
> +    blocks[elementPos+1] = blocks[elementPos+1] &  (~0L >>> endBits)
> +        | (value << (BLOCK_SIZE - endBits));
>   }
>
> +
>   @Override
>   public String toString() {
>     return "Packed64(bitsPerValue=" + bitsPerValue + ", size="
> -            + size() + ", maxPos=" + maxPos
> -            + ", elements.length=" + blocks.length + ")";
> +            + size() + ", elements.length=" + blocks.length + ")";
>   }
>
> +  @Override
>   public long ramBytesUsed() {
>     return RamUsageEstimator.sizeOf(blocks);
>   }
> @@ -226,7 +177,7 @@ class Packed64 extends PackedInts.Mutabl
>     assert fromIndex <= toIndex;
>
>     // minimum number of values that use an exact number of full blocks
> -    final int nAlignedValues = 64 / pgcd(64, bitsPerValue);
> +    final int nAlignedValues = 64 / gcd(64, bitsPerValue);
>     final int span = toIndex - fromIndex;
>     if (span <= 3 * nAlignedValues) {
>       // there needs be at least 2 * nAlignedValues aligned values for the
> @@ -270,6 +221,17 @@ class Packed64 extends PackedInts.Mutabl
>     }
>   }
>
> +  private static int gcd(int a, int b) {
> +    if (a < b) {
> +      return gcd(b, a);
> +    } else if (b == 0) {
> +      return a;
> +    } else {
> +      return gcd(b, a % b);
> +    }
> +  }
> +
> +  @Override
>   public void clear() {
>     Arrays.fill(blocks, 0L);
>   }
>
>



-- 
lucidimagination.com

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@lucene.apache.org
For additional commands, e-mail: dev-help@lucene.apache.org