Hi all,

In our work with H2 at the State and University Library of Denmark we
encountered a strange performance decrease when inserting a certain kind of
records into our H2 database. Digging into H2 with the VisualVM profiler I
discovered that whopping 89% of the running time was spent in
BitField.getLastSetBit()!

I think what triggered this was that the new records inserted (really
OAI-PMH harvested abstracts) where bigger than our usual stuff which in turn
triggered some new code paths in H2 (writing the blob content to an external
file?).

Anyways - I added a cache to BitField.getLastSetBit() which dropped the 89%
running time down below 0.1%. This in turn provided us with almost x2.2
overall performance boost when inserting these kind of records.

I also have a patch for the 1.0.79 series of H2 - if anyone is interested
please email me or ping me here.

-- 
Cheers,
Mikkel Kamstrup Erlandsen

Software Developer at
State and University Library of Denmark

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "H2 
Database" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to 
[email protected]
For more options, visit this group at 
http://groups.google.com/group/h2-database?hl=en
-~----------~----~----~----~------~----~------~--~---

Index: src/main/org/h2/util/BitField.java
===================================================================
--- src/main/org/h2/util/BitField.java	(revision 1275)
+++ src/main/org/h2/util/BitField.java	(working copy)
@@ -15,6 +15,13 @@
     private static final int BITS = 64;
     private static final int ADDRESS_MASK = BITS - 1;
     private long[] data = new long[10];
+    
+    /**
+     * Used to cache the value for the {...@link #getLastSetBit} method.
+     */
+    private int lastSetBitIndex = UNSET;
+    private static final int UNSET = -1;
+    private static final int DIRTY = -2;
 
     /**
      * Get the index of the last bit that is set.
@@ -22,14 +29,18 @@
      * @return the index of the last enabled bit, or -1
      */
     public int getLastSetBit() {
-        int i = (data.length << ADDRESS_BITS) - 1;
-        while (i >= 0) {
-            if (get(i)) {
-                return i;
-            }
-            i--;
-        }
-        return -1;
+        if (lastSetBitIndex == DIRTY) {
+            int i = (data.length << ADDRESS_BITS) - 1;
+            while (i >= 0) {
+                if (get(i)) {
+                    lastSetBitIndex = i;
+                }
+                i--;
+            }
+            lastSetBitIndex = UNSET;
+        }
+
+        return lastSetBitIndex;
     }
 
     /**
@@ -53,7 +64,7 @@
                 }
             }
         }
-        return -1;
+        return UNSET;
     }
 
     /**
@@ -130,6 +141,11 @@
      * @param x the next 8 bits (0 - 255)
      */
     public void setByte(int i, int x) {
+        // Mark the last set bit cache dirty only if we absolutely must
+        if (lastSetBitIndex < i+8) {
+            lastSetBitIndex = DIRTY;
+        }
+        
         int addr = getAddress(i);
         checkCapacity(addr);
         data[addr] |= ((long) x) << (i & (7 << 3));
@@ -141,6 +157,10 @@
      * @param i the index
      */
     public void set(int i) {
+        // Update the last set bit cache only if we absolutely must
+        if (lastSetBitIndex < i) {
+            lastSetBitIndex = i;
+        }        
         int addr = getAddress(i);
         checkCapacity(addr);
         data[addr] |= getBitMask(i);
@@ -152,6 +172,11 @@
      * @param i the index
      */
     public void clear(int i) {
+        // If the last set bit is flipped we need to recalculate the index
+        if (i == lastSetBitIndex) {
+            lastSetBitIndex = DIRTY;
+        }
+        
         int addr = getAddress(i);
         if (addr >= data.length) {
             return;

Reply via email to