[poppler] change font cache policy to improve performance.

Sam Liao phyomh at gmail.com
Sat Aug 27 22:31:10 PDT 2011


Not quite sure part of this fix which remove a bunch of
xMin/yMin/xMax/yMax related code would
lead any side effect I do not know, but they make the code more clean.

-Sam

On Sun, Aug 28, 2011 at 1:26 PM, Sam Liao <phyomh at gmail.com> wrote:
> Most of the efforts of font rendering is wasted on makeGlyph as
> for the cache mechinism, this fix tries to use a unlimited cache
> to improve the performance. Testing result of the perf-test program
> showes up it reduces about more than 25% running time.
>
> Possible further improvement not added:
> - SplashFontEngine cache size/mechanism. This is also a fixed-size
> cache.
> ---
>  splash/SplashFTFont.cc     |   65 -------------------
>  splash/SplashFTFontFile.cc |    1 -
>  splash/SplashFont.cc       |  153 +++++++++----------------------------------
>  splash/SplashFont.h        |   46 ++++++++-----
>  splash/SplashT1FontFile.cc |    1 -
>  5 files changed, 61 insertions(+), 205 deletions(-)
>
> diff --git a/splash/SplashFTFont.cc b/splash/SplashFTFont.cc
> index eea3d64..d8edb18 100644
> --- a/splash/SplashFTFont.cc
> +++ b/splash/SplashFTFont.cc
> @@ -62,8 +62,6 @@ SplashFTFont::SplashFTFont(SplashFTFontFile
> *fontFileA, SplashCoord *matA,
>   enableSlightHinting(fontFileA->engine->enableSlightHinting)
>  {
>   FT_Face face;
> -  double div;
> -  int x, y;
>
>   face = fontFileA->face;
>   if (FT_New_Size(face, &sizeObj)) {
> @@ -81,69 +79,6 @@ SplashFTFont::SplashFTFont(SplashFTFontFile
> *fontFileA, SplashCoord *matA,
>   // arithmetic doesn't work so well
>   textScale = splashSqrt(textMat[2]*textMat[2] + textMat[3]*textMat[3]) / size;
>
> -  div = face->bbox.xMax > 20000 ? 65536 : 1;
> -
> -  // transform the four corners of the font bounding box -- the min
> -  // and max values form the bounding box of the transformed font
> -  x = (int)((mat[0] * face->bbox.xMin + mat[2] * face->bbox.yMin) /
> -           (div * face->units_per_EM));
> -  xMin = xMax = x;
> -  y = (int)((mat[1] * face->bbox.xMin + mat[3] * face->bbox.yMin) /
> -           (div * face->units_per_EM));
> -  yMin = yMax = y;
> -  x = (int)((mat[0] * face->bbox.xMin + mat[2] * face->bbox.yMax) /
> -           (div * face->units_per_EM));
> -  if (x < xMin) {
> -    xMin = x;
> -  } else if (x > xMax) {
> -    xMax = x;
> -  }
> -  y = (int)((mat[1] * face->bbox.xMin + mat[3] * face->bbox.yMax) /
> -           (div * face->units_per_EM));
> -  if (y < yMin) {
> -    yMin = y;
> -  } else if (y > yMax) {
> -    yMax = y;
> -  }
> -  x = (int)((mat[0] * face->bbox.xMax + mat[2] * face->bbox.yMin) /
> -           (div * face->units_per_EM));
> -  if (x < xMin) {
> -    xMin = x;
> -  } else if (x > xMax) {
> -    xMax = x;
> -  }
> -  y = (int)((mat[1] * face->bbox.xMax + mat[3] * face->bbox.yMin) /
> -           (div * face->units_per_EM));
> -  if (y < yMin) {
> -    yMin = y;
> -  } else if (y > yMax) {
> -    yMax = y;
> -  }
> -  x = (int)((mat[0] * face->bbox.xMax + mat[2] * face->bbox.yMax) /
> -           (div * face->units_per_EM));
> -  if (x < xMin) {
> -    xMin = x;
> -  } else if (x > xMax) {
> -    xMax = x;
> -  }
> -  y = (int)((mat[1] * face->bbox.xMax + mat[3] * face->bbox.yMax) /
> -           (div * face->units_per_EM));
> -  if (y < yMin) {
> -    yMin = y;
> -  } else if (y > yMax) {
> -    yMax = y;
> -  }
> -  // This is a kludge: some buggy PDF generators embed fonts with
> -  // zero bounding boxes.
> -  if (xMax == xMin) {
> -    xMin = 0;
> -    xMax = (int)size;
> -  }
> -  if (yMax == yMin) {
> -    yMin = 0;
> -    yMax = (int)((SplashCoord)1.2 * size);
> -  }
> -
>   // compute the transform matrix
>  #if USE_FIXEDPOINT
>   matrix.xx = (FT_Fixed)((mat[0] / size).getRaw());
> diff --git a/splash/SplashFTFontFile.cc b/splash/SplashFTFontFile.cc
> index 160481e..40aac17 100644
> --- a/splash/SplashFTFontFile.cc
> +++ b/splash/SplashFTFontFile.cc
> @@ -132,7 +132,6 @@ SplashFont *SplashFTFontFile::makeFont(SplashCoord *mat,
>   SplashFont *font;
>
>   font = new SplashFTFont(this, mat, textMat);
> -  font->initCache();
>   return font;
>  }
>
> diff --git a/splash/SplashFont.cc b/splash/SplashFont.cc
> index 2bfcdc8..58ae214 100644
> --- a/splash/SplashFont.cc
> +++ b/splash/SplashFont.cc
> @@ -32,15 +32,6 @@
>  #include "SplashFont.h"
>
>  //------------------------------------------------------------------------
> -
> -struct SplashFontCacheTag {
> -  int c;
> -  short xFrac, yFrac;          // x and y fractions
> -  int mru;                     // valid bit (0x80000000) and MRU index
> -  int x, y, w, h;              // offset and size of glyph
> -};
> -
> -//------------------------------------------------------------------------
>  // SplashFont
>  //------------------------------------------------------------------------
>
> @@ -57,106 +48,45 @@ SplashFont::SplashFont(SplashFontFile *fontFileA,
> SplashCoord *matA,
>   textMat[2] = textMatA[2];
>   textMat[3] = textMatA[3];
>   aa = aaA;
> -
> -  cache = NULL;
> -  cacheTags = NULL;
> -
> -  xMin = yMin = xMax = yMax = 0;
> -}
> -
> -void SplashFont::initCache() {
> -  int i;
> -
> -  // this should be (max - min + 1), but we add some padding to
> -  // deal with rounding errors
> -  glyphW = xMax - xMin + 3;
> -  glyphH = yMax - yMin + 3;
> -  if (aa) {
> -    glyphSize = glyphW * glyphH;
> -  } else {
> -    glyphSize = ((glyphW + 7) >> 3) * glyphH;
> -  }
> -
> -  // set up the glyph pixmap cache
> -  cacheAssoc = 8;
> -  if (glyphSize <= 64) {
> -    cacheSets = 32;
> -  } else if (glyphSize <= 128) {
> -    cacheSets = 16;
> -  } else if (glyphSize <= 256) {
> -    cacheSets = 8;
> -  } else if (glyphSize <= 512) {
> -    cacheSets = 4;
> -  } else if (glyphSize <= 1024) {
> -    cacheSets = 2;
> -  } else {
> -    cacheSets = 1;
> -  }
> -  cache = (Guchar *)gmallocn_checkoverflow(cacheSets* cacheAssoc, glyphSize);
> -  if (cache != NULL) {
> -    cacheTags = (SplashFontCacheTag *)gmallocn(cacheSets * cacheAssoc,
> -                                            sizeof(SplashFontCacheTag));
> -    for (i = 0; i < cacheSets * cacheAssoc; ++i) {
> -      cacheTags[i].mru = i & (cacheAssoc - 1);
> -    }
> -  } else {
> -    cacheAssoc = 0;
> -  }
>  }
>
>  SplashFont::~SplashFont() {
>   fontFile->decRefCnt();
> -  if (cache) {
> -    gfree(cache);
> -  }
> -  if (cacheTags) {
> -    gfree(cacheTags);
> -  }
>  }
>
>  GBool SplashFont::getGlyph(int c, int xFrac, int yFrac,
>                           SplashGlyphBitmap *bitmap, int x0, int y0, SplashClip *clip,
> SplashClipResult *clipRes) {
>   SplashGlyphBitmap bitmap2;
>   int size;
> -  Guchar *p;
> -  int i, j, k;
> +  SplashFontCacheKey key;
> +  SplashFontCacheEntity ent;
>
>   // no fractional coordinates for large glyphs or non-anti-aliased
>   // glyphs
> -  if (!aa || glyphH > 50) {
> +  if (!aa) {
>     xFrac = yFrac = 0;
>   }
>
> -  // check the cache
> -  i = (c & (cacheSets - 1)) * cacheAssoc;
> -  for (j = 0; j < cacheAssoc; ++j) {
> -    if ((cacheTags[i+j].mru & 0x80000000) &&
> -       cacheTags[i+j].c == c &&
> -       (int)cacheTags[i+j].xFrac == xFrac &&
> -       (int)cacheTags[i+j].yFrac == yFrac) {
> -      bitmap->x = cacheTags[i+j].x;
> -      bitmap->y = cacheTags[i+j].y;
> -      bitmap->w = cacheTags[i+j].w;
> -      bitmap->h = cacheTags[i+j].h;
> -      for (k = 0; k < cacheAssoc; ++k) {
> -       if (k != j &&
> -           (cacheTags[i+k].mru & 0x7fffffff) <
> -             (cacheTags[i+j].mru & 0x7fffffff)) {
> -         ++cacheTags[i+k].mru;
> -       }
> -      }
> -      cacheTags[i+j].mru = 0x80000000;
> +  key.c = c;
> +  key.xFrac = (short) xFrac;
> +  key.yFrac = (short) yFrac;
> +
> +  SplashFontCache::iterator iter = cache.find(key);
> +
> +  if (iter != cache.end()) {
> +      bitmap->x = iter->second.x;
> +      bitmap->y = iter->second.y;
> +      bitmap->w = iter->second.w;
> +      bitmap->h = iter->second.h;
>       bitmap->aa = aa;
> -      bitmap->data = cache + (i+j) * glyphSize;
> +      bitmap->data = iter->second.data.get();
>       bitmap->freeData = gFalse;
>
>       *clipRes = clip->testRect(x0 - bitmap->x,
>                                 y0 - bitmap->y,
>                                 x0 - bitmap->x + bitmap->w - 1,
>                                 y0 - bitmap->y + bitmap->h - 1);
> -
>       return gTrue;
> -    }
>   }
>
>   // generate the glyph bitmap
> @@ -171,49 +101,30 @@ GBool SplashFont::getGlyph(int c, int xFrac, int yFrac,
>     return gTrue;
>   }
>
> -  // if the glyph doesn't fit in the bounding box, return a temporary
> -  // uncached bitmap
> -  if (bitmap2.w > glyphW || bitmap2.h > glyphH) {
> -    *bitmap = bitmap2;
> -    return gTrue;
> -  }
> -
>   // insert glyph pixmap in cache
>   if (aa) {
>     size = bitmap2.w * bitmap2.h;
>   } else {
>     size = ((bitmap2.w + 7) >> 3) * bitmap2.h;
>   }
> -  p = NULL; // make gcc happy
> -  if (cacheAssoc == 0)
> -  {
> -    // we had problems on the malloc of the cache, so ignore it
> -    *bitmap = bitmap2;
> -  }
> -  else
> -  {
> -    for (j = 0; j < cacheAssoc; ++j) {
> -      if ((cacheTags[i+j].mru & 0x7fffffff) == cacheAssoc - 1) {
> -        cacheTags[i+j].mru = 0x80000000;
> -        cacheTags[i+j].c = c;
> -        cacheTags[i+j].xFrac = (short)xFrac;
> -        cacheTags[i+j].yFrac = (short)yFrac;
> -        cacheTags[i+j].x = bitmap2.x;
> -        cacheTags[i+j].y = bitmap2.y;
> -        cacheTags[i+j].w = bitmap2.w;
> -        cacheTags[i+j].h = bitmap2.h;
> -        p = cache + (i+j) * glyphSize;
> -        memcpy(p, bitmap2.data, size);
> -      } else {
> -        ++cacheTags[i+j].mru;
> +
> +  ent.x = bitmap2.x;
> +  ent.y = bitmap2.y;
> +  ent.w = bitmap2.w;
> +  ent.h = bitmap2.h;
> +  ent.data = GucharPtr((Guchar *)gmallocn_checkoverflow (1, size), gfree);
> +  if (ent.data.get() != NULL) {
> +      memcpy(ent.data.get(), bitmap2.data, size);
> +      cache[key] = ent;
> +
> +      *bitmap = bitmap2;
> +      bitmap->data = ent.data.get();
> +      bitmap->freeData = gFalse;
> +      if (bitmap2.freeData) {
> +         gfree(bitmap2.data);
>       }
> -    }
> -    *bitmap = bitmap2;
> -    bitmap->data = p;
> -    bitmap->freeData = gFalse;
> -    if (bitmap2.freeData) {
> -      gfree(bitmap2.data);
> -    }
> +  } else {
> +      *bitmap = bitmap2;
>   }
>   return gTrue;
>  }
> diff --git a/splash/SplashFont.h b/splash/SplashFont.h
> index 78b00d2..1f78dbc 100644
> --- a/splash/SplashFont.h
> +++ b/splash/SplashFont.h
> @@ -26,11 +26,13 @@
>  #endif
>
>  #include "goo/gtypes.h"
> +#include "goo/GooString.h"
>  #include "SplashTypes.h"
>  #include "SplashClip.h"
> +#include <tr1/memory>
> +#include <map>
>
>  struct SplashGlyphBitmap;
> -struct SplashFontCacheTag;
>  class SplashFontFile;
>  class SplashPath;
>
> @@ -44,6 +46,31 @@ class SplashPath;
>                        ((SplashCoord)1 / (SplashCoord)splashFontFraction)
>
>  //------------------------------------------------------------------------
> +
> +struct SplashFontCacheKey {
> +  int c;
> +  short xFrac, yFrac;          // x and y fractions
> +
> +  bool operator <(const SplashFontCacheKey& b) const {
> +      if (c != b.c)
> +         return c < b.c;
> +      if (xFrac != b.xFrac)
> +         return xFrac < b.xFrac;
> +      if (yFrac != b.yFrac)
> +         return yFrac < b.yFrac;
> +      return false;
> +  }
> +};
> +
> +typedef std::tr1::shared_ptr<Guchar> GucharPtr;
> +struct SplashFontCacheEntity {
> +  int x, y, w, h;              // offset and size of glyph
> +  GucharPtr data;
> +};
> +
> +typedef std::map<struct SplashFontCacheKey, struct
> SplashFontCacheEntity> SplashFontCache;
> +
> +//------------------------------------------------------------------------
>  // SplashFont
>  //------------------------------------------------------------------------
>
> @@ -53,10 +80,6 @@ public:
>   SplashFont(SplashFontFile *fontFileA, SplashCoord *matA,
>             SplashCoord *textMatA, GBool aaA);
>
> -  // This must be called after the constructor, so that the subclass
> -  // constructor has a chance to compute the bbox.
> -  void initCache();
> -
>   virtual ~SplashFont();
>
>   SplashFontFile *getFontFile() { return fontFile; }
> @@ -96,10 +119,6 @@ public:
>   // Return the font transform matrix.
>   SplashCoord *getMatrix() { return mat; }
>
> -  // Return the glyph bounding box.
> -  void getBBox(int *xMinA, int *yMinA, int *xMaxA, int *yMaxA)
> -    { *xMinA = xMin; *yMinA = yMin; *xMaxA = xMax; *yMaxA = yMax; }
> -
>  protected:
>
>   SplashFontFile *fontFile;
> @@ -108,14 +127,7 @@ protected:
>   SplashCoord textMat[4];      // text transform matrix
>                                //   (text space -> user space)
>   GBool aa;                    // anti-aliasing
> -  int xMin, yMin, xMax, yMax;  // glyph bounding box
> -  Guchar *cache;               // glyph bitmap cache
> -  SplashFontCacheTag *         // cache tags
> -    cacheTags;
> -  int glyphW, glyphH;          // size of glyph bitmaps
> -  int glyphSize;               // size of glyph bitmaps, in bytes
> -  int cacheSets;               // number of sets in cache
> -  int cacheAssoc;              // cache associativity (glyphs per set)
> +  SplashFontCache cache;
>  };
>
>  #endif
> diff --git a/splash/SplashT1FontFile.cc b/splash/SplashT1FontFile.cc
> index 3f46ba6..45059a1 100644
> --- a/splash/SplashT1FontFile.cc
> +++ b/splash/SplashT1FontFile.cc
> @@ -127,7 +127,6 @@ SplashFont *SplashT1FontFile::makeFont(SplashCoord *mat,
>   SplashFont *font;
>
>   font = new SplashT1Font(this, mat, textMat);
> -  font->initCache();
>   return font;
>  }
>
> --
> 1.7.4.1
>


More information about the poppler mailing list