[Fontconfig] [PATCH] Cache most recent regcomp() call in _FcStrRegexCmp().
Nick Alcock
nick.alcock at oracle.com
Wed May 1 14:29:58 PDT 2013
regcomp() is not a notably fast function call. As the name suggests it is
basically a compiler, and even for calls with literal strings it is a fairly
intricate one. In particular it calls malloc() more than once, and malloc()
itself is not a particularly fast function call, especially not when the arena
is full.
Under the circumstances, it is unfortunate that _FcStrRegexCmp() is called so
very often by fontconfig. On my system (with ~5000 fonts, and a stripped-down
fonts.conf), mapping a new Emacs frame makes twelve calls to FcFontMatch()...
and those twelve calls proceed to call _FcStrRegexCmp() 175171 times, with a
call to regcomp() every time! This then proceeds to invoke malloc() on the
order of three million times. It is fairly easy to turn this into a
pathological slowdown at runtime. My Emacsen are not small (arena sizes of a
gigabyte-plus are common, with a highly fragmented heap), and in that situation
those three million malloc() and free() calls can easily consume in excess of
fifty seconds (though on a fresh startup it takes 'only' a quarter of a second
or so).
Since each call to FcFontMatch() in this pathological case (and probably in most
other cases) calls _FcStrRegexCmp() with a constant argument, a trivial cache
consisting of a single static variable suffices to reduce the number of calls to
regcomp() to... eleven. (The precise method used may need a little rethinking
for thread-safety, but the general principle, of reducing regcomp() calls via
some variety of caching, appears to be essential.)
---
src/fcstr.c | 31 +++++++++++++++++++++++--------
1 file changed, 23 insertions(+), 8 deletions(-)
[Resent for the second time: if this doesn't get through I will consider
the fontconfig list broken...]
diff --git a/src/fcstr.c b/src/fcstr.c
index 339a346..fa4560d 100644
--- a/src/fcstr.c
+++ b/src/fcstr.c
@@ -247,19 +247,32 @@ static FcBool
_FcStrRegexCmp (const FcChar8 *s, const FcChar8 *regex, int cflags, int eflags)
{
int ret = -1;
- regex_t reg;
+ static regex_t reg;
+ static FcChar8 *last;
- if ((ret = regcomp (®, (const char *)regex, cflags)) != 0)
+ if (!regex || !last || FcStrCmp (regex, last) != 0)
{
- if (FcDebug () & FC_DBG_MATCHV)
+ if (last != NULL)
{
- char buf[512];
+ regfree (®);
+ last = NULL;
+ }
- regerror (ret, ®, buf, 512);
- printf("Regexp compile error: %s\n", buf);
+ if ((ret = regcomp (®, (const char *)regex, cflags)) != 0)
+ {
+ if (FcDebug () & FC_DBG_MATCHV)
+ {
+ char buf[512];
+
+ regerror (ret, ®, buf, 512);
+ printf("Regexp compile error: %s\n", buf);
+ }
+ return FcFalse;
}
- return FcFalse;
+ FcFree (last);
+ last = FcStrdup (regex);
}
+
ret = regexec (®, (const char *)s, 0, NULL, eflags);
if (ret != 0)
{
@@ -271,7 +284,9 @@ _FcStrRegexCmp (const FcChar8 *s, const FcChar8 *regex, int cflags, int eflags)
printf("Regexp exec error: %s\n", buf);
}
}
- regfree (®);
+
+ if (last == NULL) /* only on OOM */
+ regfree (®);
return ret == 0 ? FcTrue : FcFalse;
}
--
1.8.2.1.162.ge277c5f
More information about the Fontconfig
mailing list