Hello,
I hope this is the right place for this. Please review, comment and possibly
apply the attached patches. With XFree4.2.x, XIM initialization takes about
66% of QApplication object initialization. This is mainly because of awful
pthread mutexes performance (to express it decently), and repeated processing
of the same data. The patches are done against XFree-4.2.x sources, but the
affected files don't show any big changes in webcvs. All patches are for
xc/lib/X11/ .
The easy ones first:
imLcPrs.patch
- this one replaces getc() with getc_unlocked() - with LinuxThreads linked
in (all KDE apps) getting rid of some locking always makes a noticeable
difference :-/ . I don't think libX11 needs anywhere locked access to files,
but this one is opened in a single thread in _XimCreateDefaultTree() anyway.
Manpage says getc_unlocked() is POSIX.1, so I hope there's no problem with
this one.
lcFile.c.patch2
- the same, but this time it's gets() with gets_unlocked() - the manpages
says that glibc includes it, but it's not standardized. Would it be possible
to include this one together with some configure check (probably something
like #ifdef __GLIBC__ )? I think LinuxThreads comes usually only with glibc.
lcFile.c.patch
- during QApplication construction, _XlcLocaleDirName() is called 5 times,
everytime doing exactly the same, so the patch add the obvious caching
imLcIm.c.patch
- this one is definitely not meant to be applied, it's more a proof of
concept patch. The idea is similar to the lcFile.c.patch, this time it's more
complicated. Every time an application starts, it loads the same Compose file
and start analyzing it, generating exactly the same structure. So the patch
basically takes this generated structure, packs it into one piece of memory,
and stores it to a file. Next time instead of generating the structure again,
it's simply mapped into memory and all pointers are corrected if the address
is different.
Having the whole structure cached also helps with the fact that its
generation is actually done twice for some reason, once when calling
XOpenIM(), once when calling XRegisterIMInstantiateCallback().
Could something like that get into the XFree cvs, and what would I have to
change? I have no idea how you handle portability when it comes to stuff like
mmap(), and some of you probably won't like things like fixing the pointers
in the structure or mapping into memory something possibly created by a
different user (the whole things almost looks like an ugly hack, doesn't it ?
;) ). I'll change this one (and the others, if needed) to meet your
requirements.
Oh, BTW, maybe some numbers. With unmodified XFree-4.2.x, XIM stuff takes
about 66% out of 100% QApplication initialization time, as I've already said.
The change in XlcLocaleDirName (lcFile.c.patch) reduces these 100% to 90%.
Changing getc() to getc_unlocked() (imLcPrs.c.patch) additionally reduces
this to 75%. Saving the processed Compose file (imLcIm.c.patch) makes it 60%,
and finally adding also the gets_unlocked() changes makes it about 55%. This
is 1GHz Athlon, so the numbers aren't that accurate, but with profiling with
valgrind makes me estimate the time approximately halved (= complete
QApplication initialization time). Now XIM initialization takes only about
18% from QApplication initialization, which looks much better
(congratulations :) ).
--
Lubos Lunak
KDE developer
---------------------------------------------------------------------
SuSE CR, s.r.o. e-mail: [EMAIL PROTECTED] , [EMAIL PROTECTED]
Drahobejlova 27 tel: +420 2 9654 2373
190 00 Praha 9 fax: +420 2 9654 2374
Czech Republic http://www.suse.cz/
--- imLcPrs.c.sav 2002-12-18 14:52:09.000000000 +0100
+++ imLcPrs.c 2002-12-18 14:53:57.000000000 +0100
@@ -90,11 +90,11 @@ nextch(fp, lastch)
c = *lastch;
*lastch = 0;
} else {
- c = getc(fp);
+ c = getc_unlocked(fp);
if (c == '\\') {
- c = getc(fp);
+ c = getc_unlocked(fp);
if (c == '\n') {
- c = getc(fp);
+ c = getc_unlocked(fp);
} else {
ungetc(c, fp);
c = '\\';
--- lcFile.c.sav 2002-12-18 14:52:09.000000000 +0100
+++ lcFile.c 2002-12-18 15:18:33.000000000 +0100
@@ -439,6 +439,17 @@ _XlcLocaleDirName(dir_name, lc_name)
static char locale_alias[] = LOCALE_ALIAS;
char *target_name = (char*)0;
char *target_dir = (char*)0;
+
+ static char* lc_name_last = 0;
+ static char* dir_name_last = 0;
+ int lc_name_size;
+ int dir_name_size;
+
+
+ if( lc_name_last != 0 && strcmp( lc_name_last, lc_name ) == 0 ) {
+ strcpy( dir_name, dir_name_last );
+ return dir_name;
+ }
xlocaledir (dir, PATH_MAX);
n = _XlcParsePath(dir, args, 256);
@@ -491,5 +502,20 @@ _XlcLocaleDirName(dir_name, lc_name)
strcat(dir_name, target_name);
if (target_name != lc_name)
Xfree(target_name);
+
+ /* cache for repeated calls */
+ lc_name_size = strlen( lc_name );
+ if( lc_name_last != 0 && strlen( lc_name_last ) < lc_name_size )
+ Xfree( lc_name_last );
+ if( lc_name_last == 0 )
+ lc_name_last = Xmalloc( lc_name_size + 1 );
+ dir_name_size = strlen( dir_name );
+ if( dir_name_last != 0 && strlen( dir_name_last ) < dir_name_size )
+ Xfree( dir_name_last );
+ if( dir_name_last == 0 )
+ dir_name_last = Xmalloc( dir_name_size + 1 );
+ strcpy( lc_name_last, lc_name );
+ strcpy( dir_name_last, dir_name );
+
return dir_name;
}
--- lcFile.c.sav2 2002-12-18 16:08:05.000000000 +0100
+++ lcFile.c 2002-12-18 16:08:17.000000000 +0100
@@ -227,7 +227,7 @@ resolve_name(
if (fp == NULL)
return NULL;
- while (fgets(buf, XLC_BUFSIZE, fp) != NULL) {
+ while (fgets_unlocked(buf, XLC_BUFSIZE, fp) != NULL) {
char *p = buf;
int n;
char *args[2], *from, *to;
--- imLcIm.c.sav 2002-12-18 15:31:52.000000000 +0100
+++ imLcIm.c 2002-12-18 15:38:51.000000000 +0100
@@ -73,11 +73,30 @@ _XimCheckIfLocalProcessing(im)
return(False);
}
+struct _XimCachedDefaultTreeStruct
+ {
+ int id;
+ off_t size;
+ int count;
+ char* addr;
+ DefTree defs[ 1 ];
+ };
+
+Private struct _XimCachedDefaultTreeStruct* _XimCachedDefaultTree_mmap = NULL;
+Private FILE* _XimCachedDefaultTreeFile = NULL;
+Private DefTree* _XimCachedDefaultTree = NULL;
+Private int _XimCachedDefaultTreeRefcount = 0;
+
Private void
XimFreeDefaultTree(top)
DefTree *top;
{
if (!top) return;
+ if( top == _XimCachedDefaultTree ) {
+ --_XimCachedDefaultTreeRefcount;
+ /* no deleting, it's a cache after all */
+ return;
+ }
if (top->succession) XimFreeDefaultTree(top->succession);
if (top->next) XimFreeDefaultTree(top->next);
if (top->mb) Xfree(top->mb);
@@ -208,22 +227,283 @@ _XimLocalSetIMValues(xim, values)
return(name);
}
+#include <sys/mman.h>
+/* TODO non-mmap() variant */
+
+Private Bool
+_XimReadCachedDefaultTree(
+ FILE** fp_cache,
+ off_t size)
+{
+ struct _XimCachedDefaultTreeStruct* m;
+ int addr_diff;
+ DefTree* pos;
+ int i;
+
+ m = mmap( NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fileno(*fp_cache), 0 );
+ if( m == NULL )
+ return False;
+ if( m->id != 0x3746 || m->size != size || m->count < 0 ) {
+ munmap( m, size );
+ }
+ _XimCachedDefaultTree_mmap = m;
+ _XimCachedDefaultTree = m->defs;
+ _XimCachedDefaultTreeRefcount = 0;
+ _XimCachedDefaultTreeFile = *fp_cache;
+ *fp_cache = NULL;
+ addr_diff = (char*)m - m->addr;
+ pos = m->defs;
+ for( i = 0;
+ i < m->count;
+ ++i, ++pos ) {
+#define FIXUP( el, type ) do { if( el != NULL ) el = (type*)((char*)el+addr_diff); } while( 0 )
+ FIXUP( pos->next, DefTree );
+ FIXUP( pos->succession, DefTree );
+ FIXUP( pos->mb, char );
+ FIXUP( pos->wc, wchar_t );
+ FIXUP( pos->utf8, char );
+ }
+ return True;
+}
+
+#define CACHE_FILE_DIR "/var/X11R6/cache"
+/*#define CACHE_FILE_DIR "/home/seli/zz/xim/wrk"*/
+
+
+Private char* _XimCachedDefaultTreeCacheFile( const char* name )
+ {
+ char* pos;
+ char* ret = Xmalloc( strlen( name ) + strlen( CACHE_FILE_DIR ) + 1 );
+ if( ret == NULL )
+ return NULL;
+ strcpy( ret, CACHE_FILE_DIR );
+ pos = ret + strlen( ret ) + 1; /* 1 = not the first slash */
+ strcat( ret, name );
+ while( *pos )
+ {
+ if( *pos == '/' )
+ *pos = '_';
+ ++pos;
+ }
+ return ret;
+ }
+
+
+Private int _XimCountItems(
+DefTree* top,
+int* chars_len,
+int* wchars_len)
+{
+ int items = 0;
+ DefTree* pos1;
+ DefTree* pos2;
+
+ if( top->mb != NULL )
+ *chars_len += strlen( top->mb ) + 1;
+ if( top->wc != NULL )
+ *wchars_len += _Xwcslen( top->wc ) + 1;
+ if( top->utf8 != NULL )
+ *chars_len += strlen( top->utf8 ) + 1;
+ for( pos1 = top;
+ pos1 != NULL;
+ pos1 = pos1->next )
+ {
+ ++items;
+ for( pos2 = pos1->succession;
+ pos2 != NULL;
+ pos2 = pos2->succession )
+ items += _XimCountItems( pos2, chars_len, wchars_len );
+ }
+ return items;
+}
+
+Private DefTree*
+_XimAddCacheItem(
+DefTree* top,
+struct _XimCachedDefaultTreeStruct* data,
+int* item_pos,
+char* chars,
+wchar_t* wchars)
+{
+ int my_item = (*item_pos)++;
+
+ data->defs[ my_item ] = *top;
+ if( top->mb != NULL )
+ {
+ data->defs[ my_item ].mb = chars;
+ strcpy( chars, top->mb );
+ chars += strlen( top->mb ) + 1;
+ }
+ if( top->wc != NULL )
+ {
+ data->defs[ my_item ].wc = wchars;
+ _Xwcscpy( wchars, top->wc );
+ wchars += _Xwcslen( top->wc ) + 1;
+ }
+ if( top->utf8 != NULL )
+ {
+ data->defs[ my_item ].utf8 = chars;
+ strcpy( chars, top->utf8 );
+ chars += strlen( top->utf8 ) + 1;
+ }
+ if( top->next != NULL )
+ data->defs[ my_item ].next =
+ _XimAddCacheItem( top->next, data, item_pos, chars, wchars );
+ if( top->succession != NULL )
+ data->defs[ my_item ].succession =
+ _XimAddCacheItem( top->succession, data, item_pos, chars, wchars );
+ return data->defs + my_item;
+}
+
+
+Private Bool
+_XimConvertDefaultTreeToCache(
+ Xim im)
+{
+ struct _XimCachedDefaultTreeStruct* data;
+ int chars_len = 0;
+ int wchars_len = 0;
+ char* chars;
+ wchar_t* wchars;
+ char* wchars_tmp;
+ int item_pos = 0;
+ int size;
+
+ int items = _XimCountItems(im->private.local.top,
+ &chars_len, &wchars_len );
+ if( items == 0 )
+ return False;
+ size = sizeof( struct _XimCachedDefaultTreeStruct )
+ + ( items - 1 ) * sizeof ( DefTree )
+ + ( wchars_len + 1 ) * sizeof( wchar_t )
+ + chars_len;
+ data = _XimCachedDefaultTree_mmap = Xmalloc( size );
+ if( data == NULL )
+ return False;
+ data->size = size;
+ data->count = items;
+ data->id = 0x3746;
+ data->addr = (char*)data;
+ wchars_tmp = ( char* )data
+ + sizeof( struct _XimCachedDefaultTreeStruct )
+ + ( items - 1 ) * sizeof ( DefTree );
+ wchars = (wchar_t*)data;
+ while( (char*)wchars < wchars_tmp )
+ ++wchars;
+ chars = (char*)(wchars+wchars_len);
+ _XimAddCacheItem( im->private.local.top, data, &item_pos, chars, wchars );
+ return True;
+}
+
+#if 0
+char aaa_off[] = " ";
+Private void print_tr(
+DefTree* top,
+int dif)
+{
+ DefTree* pos1;
+ DefTree* pos2;
+
+ fprintf( stderr, aaa_off + dif );
+ fprintf( stderr, "+ %p\n", top );
+ --dif;
+ for( pos1 = top;
+ pos1 != NULL;
+ pos1 = pos1->next )
+ {
+ fprintf( stderr, aaa_off + dif );
+ fprintf( stderr, "! %p\n", pos1 );
+ for( pos2 = pos1->succession;
+ pos2 != NULL;
+ pos2 = pos2->succession )
+ {
+ fprintf( stderr, aaa_off + dif );
+ fprintf( stderr, "* %p\n", pos2 );
+ print_tr(pos2,dif);
+ }
+ }
+ ++dif;
+ fprintf( stderr, aaa_off + dif );
+ fprintf( stderr, "- %p\n", top );
+}
+#endif
+
+Private void
+_XimWriteCachedDefaultTree(
+ const char* name,
+ Xim im)
+{
+ FILE* fp_cache;
+
+#if 0
+ print_tr(im->private.local.top,strlen(aaa_off));
+#endif
+
+/* TODO mozna tady dalsi mutex zamykani? */
+ if( !_XimConvertDefaultTreeToCache(im))
+ return;
+ fp_cache = _XFopenFile( name, "w" );
+ if( fp_cache == NULL )
+ return;
+ fwrite( _XimCachedDefaultTree_mmap,
+ _XimCachedDefaultTree_mmap->size, 1, fp_cache );
+ fclose( fp_cache );
+}
+
Private void
_XimCreateDefaultTree(im)
Xim im;
{
FILE *fp;
char *name;
+ FILE* fp_cache;
+ char *cache_name;
name = _XlcFileName(im->core.lcd, COMPOSE_FILE);
if (name == (char *)NULL)
return;
fp = _XFopenFile (name, "r");
- Xfree(name);
- if (fp == (FILE *)NULL)
+ if (fp == (FILE *)NULL) {
+ Xfree(name);
return;
+ }
+ /* convert name to cached filename */
+ cache_name = _XimCachedDefaultTreeCacheFile( name );
+ Xfree(name);
+ if( cache_name != NULL ) {
+ fp_cache = _XFopenFile( cache_name, "r" );
+ if( fp_cache != (FILE*)NULL ) {
+ struct stat st, st_cache;
+ if (fstat (fileno (fp), &st) != -1
+ && fstat( fileno( fp_cache ), &st_cache ) != -1
+ && st.st_mtime < st_cache.st_mtime
+ && st_cache.st_mtime > time( NULL ) - 60 * 60 * 24 ) {
+ if( _XimCachedDefaultTree != NULL
+ /* TODO test spravneho jmena locale souboru
+ pokud je jmeno spatne, a refcount == 0, jde nahradit */
+ || _XimReadCachedDefaultTree(&fp_cache, st_cache.st_size)) {
+ im->private.local.top = _XimCachedDefaultTree;
+#if 0
+ print_tr(im->private.local.top,strlen(aaa_off));
+#endif
+ ++_XimCachedDefaultTreeRefcount;
+ fclose(fp);
+ if( fp_cache != NULL )
+ fclose(fp_cache);
+ Xfree(cache_name);
+ return;
+ }
+ }
+ fclose(fp_cache);
+ }
+ }
_XimParseStringFile(fp, &im->private.local.top);
fclose(fp);
+ if( cache_name != NULL ) {
+ if( _XimCachedDefaultTree == NULL )
+ _XimWriteCachedDefaultTree(cache_name,im);
+ Xfree( cache_name );
+ }
}
Private XIMMethodsRec Xim_im_local_methods = {