Title: [128237] trunk/Source/WebCore
Revision
128237
Author
[email protected]
Date
2012-09-11 16:18:40 -0700 (Tue, 11 Sep 2012)

Log Message

IndexedDB: Optimize key decode and comparison operations
https://bugs.webkit.org/show_bug.cgi?id=96037

Reviewed by Tony Chang.

Eliminate memory allocations in code identified as CPU bottlenecks in IDB profiling.

No new tests - just performance work.

* Modules/indexeddb/IDBLevelDBCoding.cpp:
(WebCore::IDBLevelDBCoding::extractEncodedIDBKey): Avoid incremental allocations.
(WebCore::IDBLevelDBCoding::compareEncodedIDBKeys): Rename confusing variables p and q.
(IDBLevelDBCoding):
(WebCore::IDBLevelDBCoding::compare): Rename from decodeAndCompare, and add specializations
for frequently encountered types (e.g. object/index data) that can be compared without a
full decode.

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (128236 => 128237)


--- trunk/Source/WebCore/ChangeLog	2012-09-11 23:17:20 UTC (rev 128236)
+++ trunk/Source/WebCore/ChangeLog	2012-09-11 23:18:40 UTC (rev 128237)
@@ -1,3 +1,22 @@
+2012-09-11  Joshua Bell  <[email protected]>
+
+        IndexedDB: Optimize key decode and comparison operations
+        https://bugs.webkit.org/show_bug.cgi?id=96037
+
+        Reviewed by Tony Chang.
+
+        Eliminate memory allocations in code identified as CPU bottlenecks in IDB profiling.
+
+        No new tests - just performance work.
+
+        * Modules/indexeddb/IDBLevelDBCoding.cpp:
+        (WebCore::IDBLevelDBCoding::extractEncodedIDBKey): Avoid incremental allocations.
+        (WebCore::IDBLevelDBCoding::compareEncodedIDBKeys): Rename confusing variables p and q.
+        (IDBLevelDBCoding):
+        (WebCore::IDBLevelDBCoding::compare): Rename from decodeAndCompare, and add specializations
+        for frequently encountered types (e.g. object/index data) that can be compared without a
+        full decode.
+
 2012-09-11  Adam Klein  <[email protected]>
 
         Unreviewed, rolling out r128075

Modified: trunk/Source/WebCore/Modules/indexeddb/IDBLevelDBCoding.cpp (128236 => 128237)


--- trunk/Source/WebCore/Modules/indexeddb/IDBLevelDBCoding.cpp	2012-09-11 23:17:20 UTC (rev 128236)
+++ trunk/Source/WebCore/Modules/indexeddb/IDBLevelDBCoding.cpp	2012-09-11 23:18:40 UTC (rev 128237)
@@ -505,9 +505,8 @@
     return 0;
 }
 
-const char* extractEncodedIDBKey(const char* start, const char* limit, Vector<char>* result)
+const char* extractEncodedIDBKey(const char* start, const char* limit, Vector<char>* result = 0)
 {
-    ASSERT(result);
     const char* p = start;
     if (p >= limit)
         return 0;
@@ -517,8 +516,7 @@
     switch (type) {
     case IDBKeyNullTypeByte:
     case IDBKeyMinKeyTypeByte:
-        *result = encodeByte(type);
-        return p;
+        break;
     case IDBKeyArrayTypeByte: {
         int64_t length;
         p = decodeVarInt(p, limit, length);
@@ -526,16 +524,12 @@
             return 0;
         if (length < 0)
             return 0;
-        result->clear();
-        result->append(start, p - start);
         while (length--) {
-            Vector<char> subkey;
-            p = extractEncodedIDBKey(p, limit, &subkey);
+            p = extractEncodedIDBKey(p, limit);
             if (!p)
                 return 0;
-            result->append(subkey);
         }
-        return p;
+        break;
     }
     case IDBKeyStringTypeByte: {
         int64_t length;
@@ -544,20 +538,25 @@
             return 0;
         if (p + length * 2 > limit)
             return 0;
-        result->clear();
-        result->append(start, p - start + length * 2);
-        return p + length * 2;
+        p += length * 2;
+        break;
     }
     case IDBKeyDateTypeByte:
     case IDBKeyNumberTypeByte:
         if (p + sizeof(double) > limit)
             return 0;
+        p += sizeof(double);
+        break;
+    }
+
+    if (result) {
+        ASSERT(p);
+        ASSERT(p <= limit);
         result->clear();
-        result->append(start, 1 + sizeof(double));
-        return p + sizeof(double);
+        result->append(start, p - start);
     }
-    ASSERT_NOT_REACHED();
-    return 0;
+
+    return p;
 }
 
 static IDBKey::Type keyTypeByteToKeyType(unsigned char type)
@@ -581,13 +580,13 @@
     return IDBKey::InvalidType;
 }
 
-int compareEncodedIDBKeys(const char*& p, const char* limitA, const char*& q, const char* limitB)
+int compareEncodedIDBKeys(const char*& ptrA, const char* limitA, const char*& ptrB, const char* limitB)
 {
-    ASSERT(&p != &q);
-    ASSERT(p < limitA);
-    ASSERT(q < limitB);
-    unsigned char typeA = *p++;
-    unsigned char typeB = *q++;
+    ASSERT(&ptrA != &ptrB);
+    ASSERT(ptrA < limitA);
+    ASSERT(ptrB < limitB);
+    unsigned char typeA = *ptrA++;
+    unsigned char typeB = *ptrB++;
 
     if (int x = IDBKey::compareTypes(keyTypeByteToKeyType(typeA), keyTypeByteToKeyType(typeB)))
         return x;
@@ -599,16 +598,16 @@
         return 0;
     case IDBKeyArrayTypeByte: {
         int64_t lengthA, lengthB;
-        p = decodeVarInt(p, limitA, lengthA);
-        if (!p)
+        ptrA = decodeVarInt(ptrA, limitA, lengthA);
+        if (!ptrA)
             return 0;
-        q = decodeVarInt(q, limitB, lengthB);
-        if (!q)
+        ptrB = decodeVarInt(ptrB, limitB, lengthB);
+        if (!ptrB)
             return 0;
         if (lengthA < 0 || lengthB < 0)
             return 0;
         for (int64_t i = 0; i < lengthA && i < lengthB; ++i) {
-            if (int cmp = compareEncodedIDBKeys(p, limitA, q, limitB))
+            if (int cmp = compareEncodedIDBKeys(ptrA, limitA, ptrB, limitB))
                 return cmp;
         }
         if (lengthA < lengthB)
@@ -618,14 +617,14 @@
         return 0;
     }
     case IDBKeyStringTypeByte:
-        return compareEncodedStringsWithLength(p, limitA, q, limitB);
+        return compareEncodedStringsWithLength(ptrA, limitA, ptrB, limitB);
     case IDBKeyDateTypeByte:
     case IDBKeyNumberTypeByte: {
         double d, e;
-        p = decodeDouble(p, limitA, &d);
-        ASSERT(p);
-        q = decodeDouble(q, limitB, &e);
-        ASSERT(q);
+        ptrA = decodeDouble(ptrA, limitA, &d);
+        ASSERT(ptrA);
+        ptrB = decodeDouble(ptrB, limitB, &e);
+        ASSERT(ptrB);
         if (d < e)
             return -1;
         if (d > e)
@@ -643,12 +642,12 @@
     ASSERT(keyA.size() >= 1);
     ASSERT(keyB.size() >= 1);
 
-    const char* p = keyA.data();
-    const char* limitA = p + keyA.size();
-    const char* q = keyB.data();
-    const char* limitB = q + keyB.size();
+    const char* ptrA = keyA.data();
+    const char* limitA = ptrA + keyA.size();
+    const char* ptrB = keyB.data();
+    const char* limitB = ptrB + keyB.size();
 
-    return compareEncodedIDBKeys(p, limitA, q, limitB);
+    return compareEncodedIDBKeys(ptrA, limitA, ptrB, limitB);
 }
 
 Vector<char> encodeIDBKeyPath(const IDBKeyPath& keyPath)
@@ -720,8 +719,9 @@
 }
 
 namespace {
+
 template<typename KeyType>
-int decodeAndCompare(const LevelDBSlice& a, const LevelDBSlice& b)
+int compare(const LevelDBSlice& a, const LevelDBSlice& b, bool ignoreDuplicates = false)
 {
     KeyType keyA;
     KeyType keyB;
@@ -733,8 +733,105 @@
 
     return keyA.compare(keyB);
 }
+
+template<>
+int compare<ExistsEntryKey>(const LevelDBSlice& a, const LevelDBSlice& b, bool ignoreDuplicates)
+{
+    KeyPrefix prefixA;
+    KeyPrefix prefixB;
+    const char* ptrA = KeyPrefix::decode(a.begin(), a.end(), &prefixA);
+    const char* ptrB = KeyPrefix::decode(b.begin(), b.end(), &prefixB);
+    ASSERT(ptrA);
+    ASSERT(ptrB);
+    ASSERT(prefixA.m_databaseId);
+    ASSERT(prefixA.m_objectStoreId);
+    ASSERT(prefixA.m_indexId == ExistsEntryKey::SpecialIndexNumber);
+    ASSERT(prefixB.m_databaseId);
+    ASSERT(prefixB.m_objectStoreId);
+    ASSERT(prefixB.m_indexId == ExistsEntryKey::SpecialIndexNumber);
+    ASSERT(ptrA != a.end());
+    ASSERT(ptrB != b.end());
+    // Prefixes are not compared - it is assumed this was already done.
+    ASSERT(!prefixA.compare(prefixB));
+
+    return compareEncodedIDBKeys(ptrA, a.end(), ptrB, b.end());
 }
 
+template<>
+int compare<ObjectStoreDataKey>(const LevelDBSlice& a, const LevelDBSlice& b, bool ignoreDuplicates)
+{
+    KeyPrefix prefixA;
+    KeyPrefix prefixB;
+    const char* ptrA = KeyPrefix::decode(a.begin(), a.end(), &prefixA);
+    const char* ptrB = KeyPrefix::decode(b.begin(), b.end(), &prefixB);
+    ASSERT(ptrA);
+    ASSERT(ptrB);
+    ASSERT(prefixA.m_databaseId);
+    ASSERT(prefixA.m_objectStoreId);
+    ASSERT(prefixA.m_indexId == ObjectStoreDataKey::SpecialIndexNumber);
+    ASSERT(prefixB.m_databaseId);
+    ASSERT(prefixB.m_objectStoreId);
+    ASSERT(prefixB.m_indexId == ObjectStoreDataKey::SpecialIndexNumber);
+    ASSERT(ptrA != a.end());
+    ASSERT(ptrB != b.end());
+    // Prefixes are not compared - it is assumed this was already done.
+    ASSERT(!prefixA.compare(prefixB));
+
+    return compareEncodedIDBKeys(ptrA, a.end(), ptrB, b.end());
+}
+
+template<>
+int compare<IndexDataKey>(const LevelDBSlice& a, const LevelDBSlice& b, bool ignoreDuplicates)
+{
+    KeyPrefix prefixA;
+    KeyPrefix prefixB;
+    const char* ptrA = KeyPrefix::decode(a.begin(), a.end(), &prefixA);
+    const char* ptrB = KeyPrefix::decode(b.begin(), b.end(), &prefixB);
+    ASSERT(ptrA);
+    ASSERT(ptrB);
+    ASSERT(prefixA.m_databaseId);
+    ASSERT(prefixA.m_objectStoreId);
+    ASSERT(prefixA.m_indexId >= MinimumIndexId);
+    ASSERT(prefixB.m_databaseId);
+    ASSERT(prefixB.m_objectStoreId);
+    ASSERT(prefixB.m_indexId >= MinimumIndexId);
+    ASSERT(ptrA != a.end());
+    ASSERT(ptrB != b.end());
+    // Prefixes are not compared - it is assumed this was already done.
+    ASSERT(!prefixA.compare(prefixB));
+
+    // index key
+    if (int x = compareEncodedIDBKeys(ptrA, a.end(), ptrB, b.end()))
+        return x;
+    if (ignoreDuplicates)
+        return 0;
+
+    // sequence number [optional]
+    int64_t sequenceNumberA = -1;
+    int64_t sequenceNumberB = -1;
+    if (ptrA != a.end())
+        ptrA = decodeVarInt(ptrA, a.end(), sequenceNumberA);
+    if (ptrB != b.end())
+        ptrB = decodeVarInt(ptrB, b.end(), sequenceNumberB);
+
+    // primar key [optional]
+    if (!ptrA || !ptrB)
+        return 0;
+    if (ptrA == a.end() && ptrB == b.end())
+        return 0;
+    if (ptrA == a.end())
+        return -1;
+    if (ptrB == b.end())
+        return 1;
+
+    if (int x = compareEncodedIDBKeys(ptrA, a.end(), ptrB, b.end()))
+        return x;
+
+    return compareInts(sequenceNumberA, sequenceNumberB);
+}
+
+}
+
 int compare(const LevelDBSlice& a, const LevelDBSlice& b, bool indexKeys)
 {
     const char* ptrA = a.begin();
@@ -766,9 +863,9 @@
         if (typeByteA <= 1)
             return 0;
         if (typeByteA == DatabaseFreeListTypeByte)
-            return decodeAndCompare<DatabaseFreeListKey>(a, b);
+            return compare<DatabaseFreeListKey>(a, b);
         if (typeByteA == DatabaseNameTypeByte)
-            return decodeAndCompare<DatabaseNameKey>(a, b);
+            return compare<DatabaseNameKey>(a, b);
     }
 
     if (prefixA.type() == KeyPrefix::DatabaseMetaData) {
@@ -781,24 +878,25 @@
         if (int x = typeByteA - typeByteB)
             return x;
 
+        // FIXME: Replace this magic number. Should it account for UserIntVersion?
         if (typeByteA <= 3)
             return 0;
 
         if (typeByteA == ObjectStoreMetaDataTypeByte)
-            return decodeAndCompare<ObjectStoreMetaDataKey>(a, b);
+            return compare<ObjectStoreMetaDataKey>(a, b);
         if (typeByteA == IndexMetaDataTypeByte)
-            return decodeAndCompare<IndexMetaDataKey>(a, b);
+            return compare<IndexMetaDataKey>(a, b);
         if (typeByteA == ObjectStoreFreeListTypeByte)
-            return decodeAndCompare<ObjectStoreFreeListKey>(a, b);
+            return compare<ObjectStoreFreeListKey>(a, b);
         if (typeByteA == IndexFreeListTypeByte)
-            return decodeAndCompare<IndexFreeListKey>(a, b);
+            return compare<IndexFreeListKey>(a, b);
         if (typeByteA == ObjectStoreNamesTypeByte)
-            return decodeAndCompare<ObjectStoreNamesKey>(a, b);
+            return compare<ObjectStoreNamesKey>(a, b);
         if (typeByteA == IndexNamesKeyTypeByte)
-            return decodeAndCompare<IndexNamesKey>(a, b);
+            return compare<IndexNamesKey>(a, b);
 
+        // FIXME: Assert not reached here?
         return 0;
-        ASSERT_NOT_REACHED();
     }
 
     if (prefixA.type() == KeyPrefix::ObjectStoreData) {
@@ -809,7 +907,7 @@
         if (ptrB == endB)
             return 1; // FIXME: This case of non-existing user keys should not have to be handled this way.
 
-        return decodeAndCompare<ObjectStoreDataKey>(a, b);
+        return compare<ObjectStoreDataKey>(a, b);
     }
     if (prefixA.type() == KeyPrefix::ExistsEntry) {
         if (ptrA == endA && ptrB == endB)
@@ -819,7 +917,7 @@
         if (ptrB == endB)
             return 1; // FIXME: This case of non-existing user keys should not have to be handled this way.
 
-        return decodeAndCompare<ExistsEntryKey>(a, b);
+        return compare<ExistsEntryKey>(a, b);
     }
     if (prefixA.type() == KeyPrefix::IndexData) {
         if (ptrA == endA && ptrB == endB)
@@ -829,16 +927,8 @@
         if (ptrB == endB)
             return 1; // FIXME: This case of non-existing user keys should not have to be handled this way.
 
-        IndexDataKey indexDataKeyA;
-        IndexDataKey indexDataKeyB;
-
-        ptrA = IndexDataKey::decode(a.begin(), endA, &indexDataKeyA);
-        ptrB = IndexDataKey::decode(b.begin(), endB, &indexDataKeyB);
-        ASSERT(ptrA);
-        ASSERT(ptrB);
-
         bool ignoreDuplicates = indexKeys;
-        return indexDataKeyA.compare(indexDataKeyB, ignoreDuplicates);
+        return compare<IndexDataKey>(a, b, ignoreDuplicates);
     }
 
     ASSERT_NOT_REACHED();
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to