unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
* [patch] cache color info for remote X sessions [Was: Emacs 21/X11 generating unbelieveable network traffic]
@ 2002-10-06  0:21 Ami Fischman
  2002-10-06  1:29 ` Miles Bader
                   ` (2 more replies)
  0 siblings, 3 replies; 22+ messages in thread
From: Ami Fischman @ 2002-10-06  0:21 UTC (permalink / raw)


[-- Attachment #1: Type: text/plain, Size: 2270 bytes --]

Following up on a couple of recent threads, I decided to add caching to the
X Color lookup code in emacs.  The way I ended up doing it was creating a
general purpose cache linked-list, and wrapping each of XAllocColor,
XQueryColor, and XQueryColors in a function that first looks for the
requested data in the linked list before issuing the X call.  Results are
quite good.  Some arbitrary timings follow.  All times over an ssh -XC link
with lbxproxy running on the remote end.  The "stock" emacs has a version
string of:  GNU Emacs 21.1.1 (i686-pc-linux-gnu, X toolkit, Xaw3d scroll
bars) of 2001-10-22 on zion.  Times are presented for this stock emacs and
for a current CVS emacs configured --without-xim and with color caching,
and for each of those, with and without my ~/.emacs file (which loads a
whole bunch of libraries).

without ~/.emacs (-q):
Std emacs 21.1
    0.24user 0.05system 0:45.31elapsed
emacs-cvs --without-xim, with color caching:
    0.25user 0.03system 0:29.80elapsed

with ~/.emacs:
emacs-21.1:
    0.96user 0.22system 1:04.65elapsed
emacs-cvs --without-xim, with color caching:
    0.94user 0.15system 0:32.40elapsed

As you can see, the big improvement is in the loading of libraries -- where
the stock emacs goes from 45s to 69s, the caching emacs goes up less than 3
seconds.  

Another obvious thing to cache would be the font data that emacs can
request, although it is much less clear to me how to cache the XExtData
list that is potentially contained in an XFontStruct (namely, how do you
know how big the private data is?  If anyone has any suggestions here, I'd
like to hear them).

This new functionality is provided via two new files (xcache.[ch]), adding
xcache.o to the XOBJ variable in src/Makefile, adding -DUSE_XCACHE to the
ALL_CFLAGS in the Makefile, and the following small patch to xterm.c:

Index: xterm.h
===================================================================
RCS file: /cvsroot/emacs/emacs/src/xterm.h,v
retrieving revision 1.136
diff -r1.136 xterm.h
36c36,39
< #endif
---
> #ifdef USE_XCACHE
> #include "xcache.h"
> #endif /* USE_XCACHE */
> #endif /* USE_X_TOOLKIT */

xcache.[ch] are attached.  

I'd be interested to hear any feedback you might have on this.

-- 
  Ami Fischman
  usenet@fischman.org


[-- Attachment #2: xcache.c --]
[-- Type: application/octet-stream, Size: 10641 bytes --]

#define XCACHE_C

#include "xcache.h"

#define DEBUG_AC 0
#define DEBUG_QC 0
#define DEBUG_LF 0
#define DEBUG_LQF 0

/*****************************************************************************
 Definitions of main xcache structure & access functions
 *****************************************************************************/
struct _xcache_record {
  unsigned int keylen, datalen;
  char *key;
  char *data;
  struct _xcache_record *nextrec;
};

struct _xcache {
  unsigned int numrecs;
  unsigned int cachesize;
  struct _xcache_record *first;
};

static struct _xcache xcache = {0, 0, NULL};

struct _xcache_record *xcache_store(unsigned int keylen, char *key, unsigned int datalen, char *data) {
  struct _xcache_record *newrec=(struct _xcache_record*)xmalloc(sizeof(struct _xcache_record));
  newrec->keylen=keylen; newrec->datalen=datalen;
  newrec->key=(char*)xmalloc(keylen);
  newrec->data=(char*)xmalloc(datalen);
  memcpy(newrec->key, key, keylen);
  memcpy(newrec->data, data, datalen);
  newrec->nextrec=xcache.first;
  xcache.first=newrec;
  xcache.numrecs++;
  xcache.cachesize+=keylen+datalen+(sizeof(unsigned int)*2);
  return newrec;
}

struct _xcache_record *xcache_find(unsigned int keylen, char *key) {
  char *ret=0;
  struct _xcache_record *recptr = xcache.first;
  while (recptr) {
    if ((recptr->keylen==keylen) && !memcmp(key, recptr->key, keylen)) {
      return recptr;
    }
    recptr=recptr->nextrec;
  }
  return NULL;
}


/*****************************************************************************
 XAllocColor
 *****************************************************************************/
/* Status is just an int, at least on XFree86 */
Status xcache_AllocColor(Display *display, Colormap colormap, XColor *screen_in_out) {
  unsigned int keylen=strlen(DisplayString(display))+sizeof(Colormap)+sizeof(XColor), datalen=0;
  unsigned char *key=(unsigned char*)xmalloc(keylen);
  unsigned char *data=NULL;
  struct _xcache_record *recptr=NULL;
  Status ret=1;

  memset(key, 0, keylen);
  screen_in_out->pad=0;   screen_in_out->flags=0;

  memcpy(key, DisplayString(display), strlen(DisplayString(display)));
  memcpy(key+strlen(DisplayString(display)), &colormap, sizeof(Colormap));
  memcpy(key+strlen(DisplayString(display))+sizeof(Colormap), screen_in_out, sizeof(XColor));

  if (recptr=xcache_find(keylen, key)) {
    memcpy(screen_in_out, recptr->data, recptr->datalen);
#if DEBUG_AC
    TIMELOG("AllocColor hit!");
#endif
  } else {
    ret=XAllocColor(display, colormap, screen_in_out);
    if (ret) {
      xcache_store(keylen, key, sizeof(XColor), (char*)screen_in_out);
    }
#if DEBUG_AC
    TIMELOG("AllocColor miss!");
    {
      static char s[1024];
      sprintf(s, "  %lu %hu %hu %hu %hhd %hhd", 
	      screen_in_out->pixel, screen_in_out->red, screen_in_out->blue, screen_in_out->green, screen_in_out->pad, screen_in_out->flags);
      TIMELOG(s);
    }
#endif
  }  
  return ret;
}


/*****************************************************************************
 XListFonts
 *****************************************************************************/
// Forward decl's.  See below for definitions.
static char **data2nameset(char *data, unsigned int datalen, int *count);
static char *nameset2data(char **nameset, int count, unsigned int *datalenp);

char **xcache_ListFonts(Display *display, char *pattern, int maxnames, int *actual_count_return) {
  unsigned int keylen=strlen(DisplayString(display))+sizeof(int)+strlen(pattern), datalen=0;
  unsigned char *key=(unsigned char*)xmalloc(keylen);
  unsigned char *data=NULL;
  struct _xcache_record *recptr=NULL;
  char **ret=NULL;

  memset(key, 0, keylen);

  /* Make key: */
  memcpy(key, DisplayString(display), strlen(DisplayString(display)));
  memcpy(key+strlen(DisplayString(display)), pattern, strlen(pattern));
  memcpy(key+strlen(DisplayString(display))+strlen(pattern), &maxnames, sizeof(int));

  if (recptr=xcache_find(keylen, key)) {
    ret=data2nameset(recptr->data, recptr->datalen, actual_count_return);
#if DEBUG_LF
    TIMELOG("ListFonts hit!");
#endif
  } else {
    ret=XListFonts(display, pattern, maxnames, actual_count_return);
    data=nameset2data(ret, *actual_count_return, &datalen);
    xcache_store(keylen, key, sizeof(datalen), data);
#if DEBUG_LF
    TIMELOG("ListFonts miss!");
    {
      static char s[1024];
      sprintf(s, "  [%s] %d",
	      pattern, maxnames);
      TIMELOG(s);
    }
#endif
  }  
  return ret;
}

char **data2nameset(char *data, unsigned int datalen, int *count) {
  char **ret=NULL;
  char *nextstr=NULL, *retspace;
  unsigned int i=0;

  memcpy(count, data, sizeof(int));
  assert(*count>0);
  data+=sizeof(int);
  ret=(char**)xmalloc(sizeof(char*)*(*count));
  datalen-=sizeof(int);
  retspace=(char*)xmalloc(sizeof(char)*datalen);
  memcpy(retspace, data, datalen);
  nextstr=data;
  for(i=0; i<*count; ++i) {
    ret[i]=nextstr;
    nextstr=strchr(nextstr,0)+(char*)1;
  }
  return ret;
}

char *nameset2data(char **nameset, int count, unsigned int *datalenp) {
  unsigned int datalen=0, i=0;
  char *data, *datap;

  datalen=sizeof(int);
  for(i=0; i< count; ++i) {
    datalen+=strlen(nameset[i])+1;
  }
  data=(char*)xmalloc(sizeof(char)*datalen);
  *datalenp=datalen;

  memcpy(data, &count, sizeof(int));

  datap=data+sizeof(int);
  for(i=0; i<count; ++i) {
    unsigned int llen=strlen(nameset[i])+1;
    memcpy(datap, nameset[i], llen);
    datap+=llen;
  }

  return data;  
}

#ifdef USE_XCACHE_LFQ
/*****************************************************************************
 XLoadQueryFont
 *****************************************************************************/


  /* XXX TODO XXX There is a big woopsie here in that I don't see how to
   * XXX TODO XXX cache the ext_data member of the XFontStruct (which is a
   * XXX TODO XXX linked list of arbitrary data bits, the size of which is
   * XXX TODO XXX not known).  Let's try just dropping the member and seeing
   * XXX TODO XXX what happens */

char *fontstruct2data(XFontStruct *f, unsigned int *datalenp);
XFontStruct *data2fontstruct(char *data, unsigned int datalen);

XFontStruct *xcache_LoadQueryFont(Display *display, char *name) {
  unsigned int keylen=strlen(DisplayString(display))+strlen(name)+1, datalen=0;
  unsigned char *key=(unsigned char*)xmalloc(keylen);
  unsigned char *data=NULL;
  struct _xcache_record *recptr=NULL;
  XFontStruct *ret=NULL;

  memset(key, 0, keylen);

  memcpy(key, DisplayString(display), strlen(DisplayString(display)));
  memcpy(key+strlen(DisplayString(display)), name, strlen(name));
  if (recptr=xcache_find(keylen, key)) {
    ret=data2fontstruct(recptr->data, recptr->datalen);
#if DEBUG_LQF
    TIMELOG("LoadQueryFont hit!");
#endif
  } else {
    ret=XLoadQueryFont(display, name);
    data=fontstruct2data(ret, &datalen);
    if (ret) {
      xcache_store(keylen, key, datalen, data);
    }
#if DEBUG_LQF
    TIMELOG("LoadQueryFont miss!");
    {
      static char s[1024];
      sprintf(s, "  [%s]", name);
      TIMELOG(s);
    }
#endif
  }  
  return ret;
}

XFontStruct *data2fontstruct(char *data, unsigned int datalen) {
  XFontStruct *ret=(XFontStruct*)xmalloc(sizeof(XFontStruct));
  XFontProp *retprop=(XFontProp*)xmalloc(sizeof(XFontProp));
  XCharStruct *retcs=(XCharStruct*)xmalloc(sizeof(XCharStruct));

  memcpy(ret, data, sizeof(XFontStruct));
  memcpy(retprop, data+sizeof(XFontStruct), sizeof(XFontProp));
  memcpy(retcs,  data+sizeof(XFontStruct)+sizeof(XFontProp), sizeof(XCharStruct));
  ret->properties=retprop;
  ret->per_char=retcs;

  return ret;
}

char *fontstruct2data(XFontStruct *f, unsigned int *datalenp) {
  unsigned int datalen, datac;
  char *data;
  XFontStruct tmp;
  
  memcpy(&tmp, f, sizeof(XFontStruct));

  tmp.ext_data=NULL;

  datalen=sizeof(XFontStruct)+sizeof(XFontProp)+sizeof(XCharStruct);
  data=(char*)xmalloc(datalen);

  memcpy(data, &tmp, sizeof(XFontStruct));
  datac+=sizeof(XFontStruct);
  /* Also the properties member is an array that should be dealt with */
  memcpy(data+datac, tmp.properties, sizeof(XFontProp));
  datac+=sizeof(XFontProp);
  memcpy(data+datac, tmp.per_char, sizeof(XCharStruct));
  
  *datalenp=datalen;
  return data;
}
#endif /* USE_XCACHE_LFQ */

/*****************************************************************************
 XGetFontProperty
 *****************************************************************************/
#if 0
/* Again, same problem here... */
Bool xcache_GetFontProperty(XFontStruct *f, Atom atom, unsigned long *ret_val) {
}
#endif

/*****************************************************************************
 XQueryColor
 *****************************************************************************/
void xcache_QueryColor(Display *display, Colormap colormap, XColor *xc) {
  unsigned int keylen=strlen(DisplayString(display))+sizeof(Colormap)+sizeof(XColor), datalen=0;
  unsigned char *key=(unsigned char*)xmalloc(keylen);
  unsigned char *data=NULL;
  struct _xcache_record *recptr=NULL;

  memset(key, 0, keylen);
  xc->pad=0; xc->flags=0; xc->red=xc->blue=xc->green=0;

  memcpy(key, DisplayString(display), strlen(DisplayString(display)));
  memcpy(key+strlen(DisplayString(display)), &colormap, sizeof(Colormap));
  memcpy(key+strlen(DisplayString(display))+sizeof(Colormap), xc, sizeof(XColor));

  if (recptr=xcache_find(keylen, key)) {
    memcpy(xc, recptr->data, recptr->datalen);
#if DEBUG_QC
    TIMELOG("QueryColor hit!");
#endif
  } else {
    XQueryColor(display, colormap, xc);
    xcache_store(keylen, key, sizeof(XColor), (char*)xc);
#if DEBUG_QC
    TIMELOG("QueryColor miss!");
    {
      static char s[1024];
      sprintf(s, "  %lu %hu %hu %hu %hhd %hhd", 
	      xc->pixel, xc->red, xc->blue, xc->green, xc->pad, xc->flags);
      TIMELOG(s);
    }
#endif
  }  

}



/*****************************************************************************
 XQueryColors
 *****************************************************************************/
void xcache_QueryColors(Display *display, Colormap colormap, XColor xc[], int ncolors) {
  int i=0;
  for(i=0; i<ncolors; ++i) {
    xcache_QueryColor(display, colormap, &xc[i]);
  }
}

/*****************************************************************************

 *****************************************************************************/

#ifdef USE_XLOG

void timerlog(char *s, unsigned int c, char *extra) {
  static FILE *logfile = NULL;
  static time_t now = 0;
  if (logfile==NULL) logfile=fopen("/tmp/timelog", "a");
  now=time(NULL);
  fprintf(logfile, "%s:%u %s %s", s, c, extra, ctime(&now));
  fflush(logfile);
}
#endif /* USE_XLOG */

[-- Attachment #3: xcache.h --]
[-- Type: application/octet-stream, Size: 1584 bytes --]

/* The xcache structure allows caching of arbitrary information.  
   We use it to save color and font information so that the X server 
   doesn't need to be queried more than once for the same question.

*/

#ifndef XCACHE_H_INCLUDED
#define XCACHE_H_INCLUDED

#ifdef USE_XCACHE

#include <X11/X.h>
#include <X11/Xlib.h>
#include <assert.h>
#include <X11/Xatom.h>

#ifdef USE_XLOG
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define TIMELOG(extra) do { \
  timerlog(__FILE__, __LINE__, extra); \
} while (0);

void timerlog(char *s, unsigned int c, char *extra);
#else
#define TIMELOG(extra) do { ; } while(0);
#endif /* USE_XLOG */

#ifndef XCACHE_C /* So that the real calls don't get messed up */
#define XAllocColor xcache_AllocColor
#define XListFonts xcache_ListFonts
/* #define XListQueryFonts xcache_ListQueryFonts NOT DONE YET! */
/* #define XGetFontProperty xcache_GetFontProperty NOT DONE YET! */
#define XQueryColor xcache_QueryColor
#define XQueryColors xcache_QueryColors
#endif /* ! XCACHE_C */

Status xcache_AllocColor(Display *display, Colormap colormap, XColor *screen_in_out);
char **xcache_ListFonts(Display *display, char *pattern, int maxnames, int *actual_count_return);
/* XFontStruct *xcache_LoadQueryFont(Display *display, char *name); */
/* Bool xcache_GetFontProperty(XFontStruct *f, Atom atom, unsigned long *ret_val); */
void xcache_QueryColor(Display *display, Colormap colormap, XColor *xc);
void xcache_QueryColors(Display *display, Colormap colormap, XColor *xc, int ncolors);

#endif /* USE_XCACHE */

#endif /* XCACHE_H_INCLUDED */

^ permalink raw reply	[flat|nested] 22+ messages in thread

end of thread, other threads:[~2002-10-08 17:21 UTC | newest]

Thread overview: 22+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2002-10-06  0:21 [patch] cache color info for remote X sessions [Was: Emacs 21/X11 generating unbelieveable network traffic] Ami Fischman
2002-10-06  1:29 ` Miles Bader
2002-10-06  5:31   ` Ami Fischman
2002-10-06  6:53     ` Miles Bader
2002-10-06 17:59     ` Jan D.
2002-10-06 20:22       ` Ami Fischman
2002-10-06 18:14     ` Ami Fischman
2002-10-07 14:53     ` Stefan Monnier
2002-10-07 15:35       ` Ami Fischman
2002-10-07 15:52         ` Stefan Monnier
2002-10-07 16:49           ` Ami Fischman
2002-10-07 17:59             ` Jan D.
2002-10-07 18:14           ` Jan D.
2002-10-07 20:07             ` Stefan Monnier
2002-10-08  5:16               ` Jan D.
2002-10-08 17:21         ` Richard Stallman
2002-10-07 15:31     ` Richard Stallman
2002-10-07 17:04       ` Ami Fischman
2002-10-08 17:21         ` Richard Stallman
2002-10-06  7:46 ` Jan D.
2002-10-06 17:33   ` Ami Fischman
2002-10-07 15:31 ` Richard Stallman

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).