Hi,
Profiling application startup performance, I noticed that libfontconfig
accounted for a significant portion of the startup time, approx. 30% for
a rather simple application like gedit.
My font directories, especially /usr/share/fonts/100dpi and
/usr/share/fonts/75dpi contain each 1900 files after a default gentoo
installation of X.org, residing on local storage.
I found two rather small optimizations to improve the startup time:
- assuming that readdir() will never return duplicate filenames within
the very same directory. Hence FcStrSetAdd() can spare the duplicate
check, which currently runs FcStrCmp() along a growing linear list of
filenames. In fact, I have not found any duplicate string being entered
to a FcStrSet at all for my system configuration.
- _FcStrSetAppend() to not reallocate and memcmp element by element, but
doing so for e. g. 64 elements at a time.
I've verified startup time using
- time gedit (average over 9 runs)
- callgrind and cachegrind
- oprofile
Applied optimizations:
- skip duplicate check in FcStrSetAppend for values originating from
readdir()
- grow FcStrSet in 64-element bulks for local FcStrSets (FcConfig layout
unaltered)
Starting gedit is measured to
Unoptimized Optimized
user[s] 0,806 0,579
sys[s] 0,062 0,062
Total Instr Fetch Cost: 1.658.683.750 895.069.820
Cachegrind D Refs: 513.917.619 312.000.436
Cachegrind Dl Misses: 8.605.632 4.954.639
The patch is attached. Comments and questions are welcome.
Thanks,
Patrick
--
--
Patrick Haller (patrick.haller@xxxxxxxxxxxxxxxxx)
>From 914760ca92a0335cd1ce9685249c664bccbd87fd Mon Sep 17 00:00:00 2001
From: Patrick Haller <patrick.haller@xxxxxxxxxxxxxxxxx>
Date: Sat, 9 Jan 2016 03:06:31 +0100
Subject: [PATCH] Optimizations in FcStrSet
Applied optimizations:
- skip duplicate check in FcStrSetAppend for values originating from readdir()
- grow FcStrSet in 64-element bulks for local FcStrSets (FcConfig layout unaltered)
Starting gedit is measured to
Unoptimized Optimized
user[s] 0,806 0,579
sys[s] 0,062 0,062
Total Instr Fetch Cost: 1.658.683.750 895.069.820
Cachegrind D Refs: 513.917.619 312.000.436
Cachegrind Dl Misses: 8.605.632 4.954.639
---
src/fccache.c | 2 +-
src/fccfg.c | 4 ++--
src/fcdir.c | 6 +++---
src/fcint.h | 11 +++++++++++
src/fcstr.c | 47 +++++++++++++++++++++++++++++++++--------------
src/fcxml.c | 2 +-
6 files changed, 51 insertions(+), 21 deletions(-)
diff --git a/src/fccache.c b/src/fccache.c
index f2b09cb..b681a5e 100644
--- a/src/fccache.c
+++ b/src/fccache.c
@@ -590,7 +590,7 @@ FcCacheTimeValid (FcConfig *config, FcCache *cache, struct stat *dir_stat)
static FcBool
FcCacheDirsValid (FcConfig *config, FcCache *cache)
{
- FcStrSet *dirs = FcStrSetCreate ();
+ FcStrSet *dirs = FcStrSetCreateEx (FCSS_GROW_BY_64);
FcBool ret = FcFalse;
const FcChar8 *sysroot = FcConfigGetSysRoot (config);
FcChar8 *d;
diff --git a/src/fccfg.c b/src/fccfg.c
index 5f8f644..9f8ee7c 100644
--- a/src/fccfg.c
+++ b/src/fccfg.c
@@ -2205,7 +2205,7 @@ FcConfigAppFontAddFile (FcConfig *config,
return FcFalse;
}
- subdirs = FcStrSetCreate ();
+ subdirs = FcStrSetCreateEx (FCSS_GROW_BY_64);
if (!subdirs)
return FcFalse;
@@ -2252,7 +2252,7 @@ FcConfigAppFontAddDir (FcConfig *config,
return FcFalse;
}
- dirs = FcStrSetCreate ();
+ dirs = FcStrSetCreateEx (FCSS_GROW_BY_64);
if (!dirs)
return FcFalse;
diff --git a/src/fcdir.c b/src/fcdir.c
index f4807dd..a046eae 100644
--- a/src/fcdir.c
+++ b/src/fcdir.c
@@ -248,7 +248,7 @@ FcDirScanConfig (FcFontSet *set,
goto bail;
}
- files = FcStrSetCreate ();
+ files = FcStrSetCreateEx (FCSS_ALLOW_DUPLICATES | FCSS_GROW_BY_64);
if (!files)
{
ret = FcFalse;
@@ -349,7 +349,7 @@ FcDirCacheScan (const FcChar8 *dir, FcConfig *config)
if (!set)
goto bail;
- dirs = FcStrSetCreate ();
+ dirs = FcStrSetCreateEx (FCSS_GROW_BY_64);
if (!dirs)
goto bail1;
@@ -404,7 +404,7 @@ FcDirCacheRescan (const FcChar8 *dir, FcConfig *config)
d = FcStrdup (dir);
if (FcStatChecksum (d, &dir_stat) < 0)
goto bail;
- dirs = FcStrSetCreate ();
+ dirs = FcStrSetCreateEx (FCSS_GROW_BY_64);
if (!dirs)
goto bail;
diff --git a/src/fcint.h b/src/fcint.h
index 8fa01d6..ee6cc99 100644
--- a/src/fcint.h
+++ b/src/fcint.h
@@ -339,11 +339,19 @@ struct _FcCharSet {
FcCharLeaf))
#define FcCharSetNumbers(c) FcOffsetMember(c,numbers_offset,FcChar16)
+#define FCSS_DEFAULT 0 /* default behavior */
+#define FCSS_ALLOW_DUPLICATES 1 /* allows for duplicate strings in the set */
+#define FCSS_GROW_BY_64 2 /* grows buffer by 64 elements instead of 1 */
+
+#define FcStrSetHasControlBit(s,c) (s->control & c)
+#define FcStrSetHasControlBits(s,c) ( (c) == (s->control & (c)) )
+
struct _FcStrSet {
FcRef ref; /* reference count */
int num;
int size;
FcChar8 **strs;
+ unsigned int control; /* control bits for set behavior */
};
struct _FcStrList {
@@ -1113,6 +1121,9 @@ FcPrivate FcBool
FcIsFsMtimeBroken (const FcChar8 *dir);
/* fcstr.c */
+FcPrivate FcStrSet *
+FcStrSetCreateEx (unsigned int control);
+
FcPrivate FcBool
FcStrSetAddLangs (FcStrSet *strs, const char *languages);
diff --git a/src/fcstr.c b/src/fcstr.c
index 29a577d..b65492d 100644
--- a/src/fcstr.c
+++ b/src/fcstr.c
@@ -880,7 +880,7 @@ FcStrBuildFilename (const FcChar8 *path,
if (!path)
return NULL;
- sset = FcStrSetCreate ();
+ sset = FcStrSetCreateEx (FCSS_ALLOW_DUPLICATES | FCSS_GROW_BY_64);
if (!sset)
return NULL;
@@ -1130,6 +1130,12 @@ FcStrCanonFilename (const FcChar8 *s)
FcStrSet *
FcStrSetCreate (void)
{
+ return FcStrSetCreateEx (FCSS_DEFAULT);
+}
+
+FcStrSet *
+FcStrSetCreateEx (unsigned int control)
+{
FcStrSet *set = malloc (sizeof (FcStrSet));
if (!set)
return 0;
@@ -1137,29 +1143,42 @@ FcStrSetCreate (void)
set->num = 0;
set->size = 0;
set->strs = 0;
+ set->control = control;
return set;
}
static FcBool
+_FcStrSetGrow (FcStrSet *set, int growElements)
+{
+ /* accommodate an additional NULL entry at the end of the array */
+ FcChar8 **strs = malloc ((set->size + growElements + 1) * sizeof (FcChar8 *));
+ if (!strs)
+ return FcFalse;
+ if (set->num)
+ memcpy (strs, set->strs, set->num * sizeof (FcChar8 *));
+ if (set->strs)
+ free (set->strs);
+ set->size = set->size + growElements;
+ set->strs = strs;
+ return FcTrue;
+}
+
+static FcBool
_FcStrSetAppend (FcStrSet *set, FcChar8 *s)
{
- if (FcStrSetMember (set, s))
+ if (!FcStrSetHasControlBit (set, FCSS_ALLOW_DUPLICATES))
{
- FcStrFree (s);
- return FcTrue;
+ if (FcStrSetMember (set, s))
+ {
+ FcStrFree (s);
+ return FcTrue;
+ }
}
if (set->num == set->size)
{
- FcChar8 **strs = malloc ((set->size + 2) * sizeof (FcChar8 *));
-
- if (!strs)
- return FcFalse;
- if (set->num)
- memcpy (strs, set->strs, set->num * sizeof (FcChar8 *));
- if (set->strs)
- free (set->strs);
- set->size = set->size + 1;
- set->strs = strs;
+ int growElements = FcStrSetHasControlBit (set, FCSS_GROW_BY_64) ? 64 : 1;
+ if (!_FcStrSetGrow(set, growElements))
+ return FcFalse;
}
set->strs[set->num++] = s;
set->strs[set->num] = 0;
diff --git a/src/fcxml.c b/src/fcxml.c
index 52a0668..cd8fff1 100644
--- a/src/fcxml.c
+++ b/src/fcxml.c
@@ -3158,7 +3158,7 @@ FcConfigParseAndLoadDir (FcConfig *config,
strcat ((char *) file, "/");
base = file + strlen ((char *) file);
- files = FcStrSetCreate ();
+ files = FcStrSetCreateEx (FCSS_GROW_BY_64);
if (!files)
{
ret = FcFalse;
--
2.7.0
_______________________________________________
Fontconfig mailing list
Fontconfig@xxxxxxxxxxxxxxxxxxxxx
http://lists.freedesktop.org/mailman/listinfo/fontconfig