Use murmur3 finisher to improve font hash efficiency.
authorreed <reed@google.com>
Wed, 28 Jan 2015 21:28:53 +0000 (13:28 -0800)
committerCommit bot <commit-bot@chromium.org>
Wed, 28 Jan 2015 21:28:53 +0000 (13:28 -0800)
Add dump() method to inspect glyphcache strikes.

Murmur addition improves hash efficient roughly 50%

BUG=skia:

Review URL: https://codereview.chromium.org/877113002

samplecode/SamplePictFile.cpp
src/core/SkChecksum.h
src/core/SkGlyphCache.cpp
src/core/SkGlyphCache.h
src/core/SkScalerContext.h

index 3fb335fc3415b02131ab955749415cee6541a698..8f827e10aa248138ad7a2b4409a64e9504e23815 100644 (file)
@@ -30,6 +30,8 @@
 #include "SkSurface.h"
 #include "SkXMLParser.h"
 
+#include "SkGlyphCache.h"
+
 class PictFileView : public SampleView {
 public:
     PictFileView(const char name[] = NULL)
@@ -89,12 +91,21 @@ protected:
         SkASSERT(static_cast<int>(fBBox) < kBBoxTypeCount);
         SkPicture** picture = fPictures + fBBox;
 
+#ifdef SK_GLYPHCACHE_TRACK_HASH_STATS
+        SkGraphics::PurgeFontCache();
+#endif
+
         if (!*picture) {
             *picture = LoadPicture(fFilename.c_str(), fBBox);
         }
         if (*picture) {
             canvas->drawPicture(*picture);
         }
+
+#ifdef SK_GLYPHCACHE_TRACK_HASH_STATS
+        SkGlyphCache::Dump();
+        SkDebugf("\n");
+#endif
     }
 
 private:
index 9f2ebf460aff1ed778183f1480a15f42b0dd1315..d9065f5ff3a18e0b99f422b3953ee10cac26c97d 100644 (file)
@@ -51,6 +51,19 @@ public:
         return hash;
     }
 
+    /**
+     * uint32_t -> uint32_t hash, useful for when you're about to trucate this hash but you
+     * suspect its low bits aren't well mixed.
+     *
+     *  This version is 2-lines cheaper than Mix, but seems to be sufficient for the font cache.
+     */
+    static uint32_t CheapMix(uint32_t hash) {
+        hash ^= hash >> 16;
+        hash *= 0x85ebca6b;
+        hash ^= hash >> 16;
+        return hash;
+    }
+
     /**
      * Calculate 32-bit Murmur hash (murmur3).
      * This should take 2-3x longer than SkChecksum::Compute, but is a considerably better hash.
index 1cbfcd8e6e9fa235610a0c459b469a1e8a4ff718..a26e9f5cd0a83cf770225946deb85ff3237cf1ee 100755 (executable)
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2006 The Android Open Source Project
  *
@@ -6,7 +5,6 @@
  * found in the LICENSE file.
  */
 
-
 #include "SkGlyphCache.h"
 #include "SkGlyphCache_Globals.h"
 #include "SkGraphics.h"
@@ -18,7 +16,6 @@
 #include "SkTypeface.h"
 
 //#define SPEW_PURGE_STATUS
-//#define RECORD_HASH_EFFICIENCY
 
 namespace {
 
@@ -43,26 +40,12 @@ static SkGlyphCache_Globals& getGlobals() {
 
 ///////////////////////////////////////////////////////////////////////////////
 
-#ifdef RECORD_HASH_EFFICIENCY
-    static uint32_t gHashSuccess;
-    static uint32_t gHashCollision;
-
-    static void RecordHashSuccess() {
-        gHashSuccess += 1;
-    }
-
-    static void RecordHashCollisionIf(bool pred) {
-        if (pred) {
-            gHashCollision += 1;
-
-            uint32_t total = gHashSuccess + gHashCollision;
-            SkDebugf("Font Cache Hash success rate: %d%%\n",
-                     100 * gHashSuccess / total);
-        }
-    }
+#ifdef SK_GLYPHCACHE_TRACK_HASH_STATS
+    #define RecordHashSuccess()             fHashHitCount += 1
+    #define RecordHashCollisionIf(pred)     do { if (pred) fHashMissCount += 1; } while (0)
 #else
-    #define RecordHashSuccess() (void)0
-    #define RecordHashCollisionIf(pred) (void)0
+    #define RecordHashSuccess()             (void)0
+    #define RecordHashCollisionIf(pred)     (void)0
 #endif
 #define RecordHashCollision() RecordHashCollisionIf(true)
 
@@ -94,6 +77,10 @@ SkGlyphCache::SkGlyphCache(SkTypeface* typeface, const SkDescriptor* desc, SkSca
     fGlyphArray.setReserve(kMinGlyphCount);
 
     fAuxProcList = NULL;
+
+#ifdef SK_GLYPHCACHE_TRACK_HASH_STATS
+    fHashHitCount = fHashMissCount = 0;
+#endif
 }
 
 SkGlyphCache::~SkGlyphCache() {
@@ -260,8 +247,7 @@ const SkGlyph& SkGlyphCache::getGlyphIDMetrics(uint16_t glyphID) {
     return *glyph;
 }
 
-const SkGlyph& SkGlyphCache::getGlyphIDMetrics(uint16_t glyphID,
-                                               SkFixed x, SkFixed y) {
+const SkGlyph& SkGlyphCache::getGlyphIDMetrics(uint16_t glyphID, SkFixed x, SkFixed y) {
     VALIDATE();
     uint32_t id = SkGlyph::MakeID(glyphID, x, y);
     unsigned index = ID2HashIndex(id);
@@ -364,6 +350,29 @@ const SkPath* SkGlyphCache::findPath(const SkGlyph& glyph) {
     return glyph.fPath;
 }
 
+void SkGlyphCache::dump() const {
+    const SkTypeface* face = fScalerContext->getTypeface();
+    const SkScalerContextRec& rec = fScalerContext->getRec();
+    SkMatrix matrix;
+    rec.getSingleMatrix(&matrix);
+    matrix.preScale(SkScalarInvert(rec.fTextSize), SkScalarInvert(rec.fTextSize));
+    SkString name;
+    face->getFamilyName(&name);
+
+    SkString msg;
+    msg.printf("cache typeface:%x %25s:%d size:%2g [%g %g %g %g] lum:%02X devG:%d pntG:%d cntr:%d glyphs:%3d",
+               face->uniqueID(), name.c_str(), face->style(), rec.fTextSize,
+               matrix[SkMatrix::kMScaleX], matrix[SkMatrix::kMSkewX],
+               matrix[SkMatrix::kMSkewY], matrix[SkMatrix::kMScaleY],
+               rec.fLumBits & 0xFF, rec.fDeviceGamma, rec.fPaintGamma, rec.fContrast,
+               fGlyphArray.count());
+#ifdef SK_GLYPHCACHE_TRACK_HASH_STATS
+    const int sum = SkTMax(fHashHitCount + fHashMissCount, 1);   // avoid divide-by-zero
+    msg.appendf(" hash:%2d\n", 100 * fHashHitCount / sum);
+#endif
+    SkDebugf("%s\n", msg.c_str());
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 bool SkGlyphCache::getAuxProcData(void (*proc)(void*), void** dataPtr) const {
@@ -520,6 +529,33 @@ void SkGlyphCache::AttachCache(SkGlyphCache* cache) {
     getGlobals().attachCacheToHead(cache);
 }
 
+void SkGlyphCache::Dump() {
+    SkGlyphCache_Globals& globals = getGlobals();
+    SkAutoMutexAcquire    ac(globals.fMutex);
+    SkGlyphCache*         cache;
+
+    globals.validate();
+
+    SkDebugf("SkGlyphCache strikes:%d memory:%d\n",
+             globals.getCacheCountUsed(), (int)globals.getTotalMemoryUsed());
+
+#ifdef SK_GLYPHCACHE_TRACK_HASH_STATS
+    int hitCount = 0;
+    int missCount = 0;
+#endif
+
+    for (cache = globals.internalGetHead(); cache != NULL; cache = cache->fNext) {
+#ifdef SK_GLYPHCACHE_TRACK_HASH_STATS
+        hitCount += cache->fHashHitCount;
+        missCount += cache->fHashMissCount;
+#endif
+        cache->dump();
+    }
+#ifdef SK_GLYPHCACHE_TRACK_HASH_STATS
+    SkDebugf("Hash hit percent:%2d\n", 100 * hitCount / (hitCount + missCount));
+#endif
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 void SkGlyphCache_Globals::attachCacheToHead(SkGlyphCache* cache) {
index bb34a7d977411784971c9658793ad787e7197237..e11a46025e1d7d5223f348ab04a4929b5069c57a 100644 (file)
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2006 The Android Open Source Project
  *
@@ -6,11 +5,11 @@
  * found in the LICENSE file.
  */
 
-
 #ifndef SkGlyphCache_DEFINED
 #define SkGlyphCache_DEFINED
 
 #include "SkBitmap.h"
+#include "SkChecksum.h"
 #include "SkChunkAlloc.h"
 #include "SkDescriptor.h"
 #include "SkGlyph.h"
@@ -23,6 +22,10 @@ class SkPaint;
 
 class SkGlyphCache_Globals;
 
+// Enable this locally to add stats for hash-table hit rates. It also extends the dump()
+// output to show those stats.
+//#define SK_GLYPHCACHE_TRACK_HASH_STATS
+
 /** \class SkGlyphCache
 
     This class represents a strike: a specific combination of typeface, size,
@@ -101,6 +104,8 @@ public:
         return fScalerContext->isSubpixel();
     }
 
+    void dump() const;
+
     /*  AuxProc/Data allow a client to associate data with this cache entry.
         Multiple clients can use this, as their data is keyed with a function
         pointer. In addition to serving as a key, the function pointer is called
@@ -145,6 +150,8 @@ public:
         return VisitCache(typeface, desc, DetachProc, NULL);
     }
 
+    static void Dump();
+
 #ifdef SK_DEBUG
     void validate() const;
 #else
@@ -204,15 +211,19 @@ private:
     // no reason to use the same kHashCount as fGlyphHash, but we do for now
     CharGlyphRec    fCharToGlyphHash[kHashCount];
 
-    static inline unsigned ID2HashIndex(uint32_t id) {
-        id ^= id >> 16;
-        id ^= id >> 8;
-        return id & kHashMask;
+    static inline unsigned ID2HashIndex(uint32_t h) {
+        return SkChecksum::CheapMix(h) & kHashMask;
     }
 
     // used to track (approx) how much ram is tied-up in this cache
     size_t  fMemoryUsed;
 
+
+#ifdef SK_GLYPHCACHE_TRACK_HASH_STATS
+    int fHashHitCount;
+    int fHashMissCount;
+#endif
+
     struct AuxProcRec {
         AuxProcRec* fNext;
         void (*fProc)(void*);
index 9d3bdc8a8f1e29e7fdedd623ea279c2f4d4d8931..4677635d7c62f15c6551116890e78186b7806e88 100644 (file)
@@ -243,6 +243,8 @@ public:
 
     static SkMaskGamma::PreBlend GetMaskPreBlend(const Rec& rec);
 
+    const Rec& getRec() const { return fRec; }
+
 protected:
     Rec         fRec;