Perf improvement for some scenarios by caching SplashPath. PDF from https://bugs.freedesktop.org/show_bug.cgi?id=11849 shows a high rate of creation/destruction of SplashPath objects. At the same time, only a few exists at the same time. This patch implements a small cache to recycle SplashPath objects. In my test rendering time for the above PDF dropped ~4%, mostly thanks to decent reduction in malloc/free calls.
diff --git a/poppler/SplashOutputDev.cc b/poppler/SplashOutputDev.cc index d8ae539..dd211f4 100644 --- a/poppler/SplashOutputDev.cc +++ b/poppler/SplashOutputDev.cc
@@ -1170,7 +1170,7 @@ } path = convertPath(state, state->getPath()); splash->stroke(path); - delete path; + SplashPath::destroy(path); } void SplashOutputDev::fill(GfxState *state) { @@ -1181,7 +1181,7 @@ } path = convertPath(state, state->getPath()); splash->fill(path, gFalse); - delete path; + SplashPath::destroy(path); } void SplashOutputDev::eoFill(GfxState *state) { @@ -1192,7 +1192,7 @@ } path = convertPath(state, state->getPath()); splash->fill(path, gTrue); - delete path; + SplashPath::destroy(path); } void SplashOutputDev::clip(GfxState *state) { @@ -1200,7 +1200,7 @@ path = convertPath(state, state->getPath()); splash->clipToPath(path, gFalse); - delete path; + SplashPath::destroy(path); } void SplashOutputDev::eoClip(GfxState *state) { @@ -1208,7 +1208,7 @@ path = convertPath(state, state->getPath()); splash->clipToPath(path, gTrue); - delete path; + SplashPath::destroy(path); } void SplashOutputDev::clipToStrokePath(GfxState *state) { @@ -1216,9 +1216,9 @@ path = convertPath(state, state->getPath()); path2 = splash->makeStrokePath(path); - delete path; + SplashPath::destroy(path); splash->clipToPath(path2, gFalse); - delete path2; + SplashPath::destroy(path2); } SplashPath *SplashOutputDev::convertPath(GfxState * /*state*/, GfxPath *path) { @@ -1226,7 +1226,7 @@ GfxSubpath *subpath; int i, j; - sPath = new SplashPath(); + sPath = SplashPath::create(); for (i = 0; i < path->getNumSubpaths(); ++i) { subpath = path->getSubpath(i); if (subpath->getNumPoints() > 0) { @@ -1291,9 +1291,9 @@ if ((render & 3) == 1 || (render & 3) == 2) { if (!state->getStrokeColorSpace()->isNonMarking()) { if ((path = font->getGlyphPath(code))) { - path->offset((SplashCoord)x, (SplashCoord)y); - splash->stroke(path); - delete path; + path->offset((SplashCoord)x, (SplashCoord)y); + splash->stroke(path); + SplashPath::destroy(path); } } } @@ -1303,10 +1303,10 @@ if ((path = font->getGlyphPath(code))) { path->offset((SplashCoord)x, (SplashCoord)y); if (textClipPath) { - textClipPath->append(path); - delete path; + textClipPath->append(path); + SplashPath::destroy(path); } else { - textClipPath = path; + textClipPath = path; } } } @@ -1585,7 +1585,7 @@ void SplashOutputDev::endTextObject(GfxState *state) { if (textClipPath) { splash->clipToPath(textClipPath, gFalse); - delete textClipPath; + SplashPath::destroy(textClipPath); textClipPath = NULL; } }
diff --git a/splash/Splash.cc b/splash/Splash.cc index 45eb6fe..84c3300 100644 --- a/splash/Splash.cc +++ b/splash/Splash.cc
@@ -1184,7 +1184,7 @@ path2 = flattenPath(path, state->matrix, state->flatness); if (state->lineDashLength > 0) { dPath = makeDashedPath(path2); - delete path2; + SplashPath::destroy(path2); path2 = dPath; } if (state->lineWidth == 0) { @@ -1192,7 +1192,7 @@ } else { strokeWide(path2); } - delete path2; + SplashPath::destroy(path2); return splashOk; } @@ -1310,7 +1310,7 @@ path2 = makeStrokePath(path, gFalse); fillWithPattern(path2, gFalse, state->strokePattern, state->strokeAlpha); - delete path2; + SplashPath::destroy(path2); } SplashPath *Splash::flattenPath(SplashPath *path, SplashCoord *matrix, @@ -1320,7 +1320,7 @@ Guchar flag; int i; - fPath = new SplashPath(); + fPath = SplashPath::create(); flatness2 = flatness * flatness; i = 0; while (i < path->length) { @@ -1451,7 +1451,7 @@ ++lineDashStartIdx; } - dPath = new SplashPath(); + dPath = SplashPath::create(); // process each subpath i = 0; @@ -3219,7 +3219,7 @@ pathIn = flattenPath(path, state->matrix, state->flatness); if (state->lineDashLength > 0) { pathOut = makeDashedPath(pathIn); - delete pathIn; + SplashPath::destroy(pathIn); pathIn = pathOut; } } else { @@ -3231,7 +3231,7 @@ left0 = left1 = right0 = right1 = join0 = join1 = 0; // make gcc happy leftFirst = rightFirst = firstPt = 0; // make gcc happy - pathOut = new SplashPath(); + pathOut = SplashPath::create(); w = state->lineWidth; for (i = 0; i < pathIn->length - 1; ++i) { @@ -3491,7 +3491,7 @@ } if (pathIn != path) { - delete pathIn; + SplashPath::destroy(pathIn); } return pathOut;
diff --git a/splash/SplashFTFont.cc b/splash/SplashFTFont.cc index 963d42d..dfda251 100644 --- a/splash/SplashFTFont.cc +++ b/splash/SplashFTFont.cc
@@ -268,7 +268,7 @@ if (FT_Get_Glyph(slot, &glyph)) { return NULL; } - path.path = new SplashPath(); + path.path = SplashPath::create(); path.textScale = textScale; path.needClose = gFalse; FT_Outline_Decompose(&((FT_OutlineGlyph)glyph)->outline,
diff --git a/splash/SplashPath.cc b/splash/SplashPath.cc index 261f778..29c5a2d 100644 --- a/splash/SplashPath.cc +++ b/splash/SplashPath.cc
@@ -15,6 +15,42 @@ #include "SplashErrorCodes.h" #include "SplashPath.h" +// According to my profiling, number of SplahPath objects created at the same +// time rarely exceeds 4, so this is a good number for the size of the cache +#define CACHE_SIZE 4 + +static int cachedCount = 0; +static SplashPath* splashPathCache[CACHE_SIZE] = { NULL }; + +SplashPath* SplashPath::create() +{ + SplashPath* result = NULL; + if (cachedCount > 0) { + result = splashPathCache[--cachedCount]; + } else { + result = new SplashPath(); + } + return result; +} + +void SplashPath::destroy(SplashPath* path) +{ + if (cachedCount < CACHE_SIZE) { + path->Reset(); + splashPathCache[cachedCount++] = path; + } + else + delete path; +} + +void SplashPath::emptyCache() +{ + for (int i=0; i < cachedCount; i++) { + delete splashPathCache[i]; + } + cachedCount = 0; +} + //------------------------------------------------------------------------ // SplashPath //------------------------------------------------------------------------ @@ -182,3 +218,12 @@ *y = pts[length - 1].y; return gTrue; } + +void SplashPath::Reset() +{ + // TODO: possibly free data if size is above some threshold to avoid + // cache eating too much memory + length = 0; + hintsLength = 0; +} +
diff --git a/splash/SplashPath.h b/splash/SplashPath.h index ea58af0..36e9b4e 100644 --- a/splash/SplashPath.h +++ b/splash/SplashPath.h
@@ -54,14 +54,13 @@ class SplashPath { public: - // Create an empty path. - SplashPath(); + static SplashPath* create(); + static void destroy(SplashPath* path); + static void emptyCache(); // Copy a path. SplashPath *copy() { return new SplashPath(this); } - ~SplashPath(); - // Append <path> to <this>. void append(SplashPath *path); @@ -96,9 +95,15 @@ // Get the current point. GBool getCurPt(SplashCoord *x, SplashCoord *y); + void Reset(); private: + // Create an empty path. + SplashPath(); SplashPath(SplashPath *path); + + ~SplashPath(); + void grow(int nPts); GBool noCurrentPoint() { return curSubpath == length; } GBool onePointSubpath() { return curSubpath == length - 1; }
diff --git a/splash/SplashT1Font.cc b/splash/SplashT1Font.cc index b30c0fc..fb2b7f0 100644 --- a/splash/SplashT1Font.cc +++ b/splash/SplashT1Font.cc
@@ -238,7 +238,7 @@ T1_TransformFont(outlineID, &matrix); } - path = new SplashPath(); + path = SplashPath::create(); if ((outline = T1_GetCharOutline(outlineID, c, outlineSize, NULL))) { x = 0; y = 0;