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;