From 4356e84222fb60f915c07d8d1f55c6eeb3d58a24 Mon Sep 17 00:00:00 2001 From: jgarcia Date: Sun, 6 Jan 2008 15:44:04 +0000 Subject: [PATCH] Use thread-local hashes instead of per-function hashes to cache the results of invoking generic functions --- src/CHANGELOG | 5 ++ src/c/gfun.d | 103 +++++++++++++++++++++----- src/c/hash.d | 167 +----------------------------------------- src/c/main.d | 11 +++ src/c/newhash.h | 146 ++++++++++++++++++++++++++++++++++++ src/c/symbols_list.h | 4 + src/c/symbols_list2.h | 4 + src/clos/fixup.lsp | 2 +- src/clos/kernel.lsp | 20 +---- src/h/external.h | 11 +++ src/h/internal.h | 5 +- src/lsp/config.lsp.in | 2 +- 12 files changed, 274 insertions(+), 206 deletions(-) create mode 100644 src/c/newhash.h diff --git a/src/CHANGELOG b/src/CHANGELOG index baf2aed39..afadd5f21 100644 --- a/src/CHANGELOG +++ b/src/CHANGELOG @@ -12,6 +12,11 @@ ECL 0.9k: - Improved hashing on 64-bit machines. +* CLOS: + + - When caching generic function calls, ECL now uses a thread-local hash table + instead of one hash table per generic function. + ;;; Local Variables: *** ;;; mode:text *** ;;; fill-column:79 *** diff --git a/src/c/gfun.d b/src/c/gfun.d index b4a9f8ea3..8889bdb4e 100644 --- a/src/c/gfun.d +++ b/src/c/gfun.d @@ -15,6 +15,54 @@ #include #include #include +#include "newhash.h" + +static cl_object +do_clear_gfun_hash(cl_object target) +{ + cl_object table = cl_env.gfun_hash; + if (target == Ct) { + cl_clrhash(table); + } else { + cl_index hsize = table->hash.size; + struct ecl_hashtable_entry *htable = table->hash.data; + for (; hsize; htable++, hsize--) { + if (htable->key != OBJNULL) { + cl_object gfun = CAR(htable->key); + if (gfun == target) { + htable->key = OBJNULL; + htable->value = OBJNULL; + table->hash.entries--; + } + } + } + } +} + +cl_object +si_clear_gfun_hash(cl_object what) +{ + /* + * This function clears the generic function call hashes selectively. + * what = Ct means clear the hash completely + * what = generic function, means cleans only these entries + * If we work on a multithreaded environment, we simply enqueue these + * operations and wait for the destination thread to update its own hash. + */ +#ifdef ECL_THREADS + cl_object list; + THREAD_OP_LOCK(); + list = cl_core.processes; + for (; list != Cnil; list = CDR(list)) { + cl_object process = CAR(list); + struct cl_env_struct *env = process->process.env; + env->gfun_hash_clear_list = CONS(what, env->gfun_hash_clear_list); + } + THREAD_OP_UNLOCK(); +#else + do_clear_gfun_hash(what); +#endif +} static void reshape_instance(cl_object x, int delta) @@ -98,20 +146,22 @@ si_generic_function_p(cl_object x) static struct ecl_hashtable_entry * get_meth_hash(cl_object *keys, int argno, cl_object hashtable) { - int hsize; + cl_index hsize; struct ecl_hashtable_entry *e, *htable; cl_object hkey, tlist; - register cl_index i = 0; - int k, n; /* k added by chou */ + register cl_index i; + int k, n; + + for (i = 0, n = 0; n < argno; n++) { + register cl_index a = (cl_index)keys[n]; + register cl_index b = GOLDEN_RATIO; + mix(a, b, i); + } hsize = hashtable->hash.size; htable = hashtable->hash.data; - for (n = 0; n < argno; n++) - i += (cl_index)keys[n] / 4; /* instead of: - i += hash_eql(keys[n]); - i += hash_eql(Cnil); - */ - for (i %= hsize, k = 0; k < hsize; i = (i + 1) % hsize, k++) { + i = i % hsize; + for (k = 0; k < hsize; k++) { bool b = 1; e = &htable[i]; hkey = e->key; @@ -122,6 +172,7 @@ get_meth_hash(cl_object *keys, int argno, cl_object hashtable) b &= (keys[n] == CAR(tlist)); if (b) return(&htable[i]); + if (++i >= hsize) i = 0; } ecl_internal_error("get_meth_hash"); } @@ -134,12 +185,14 @@ set_meth_hash(cl_object *keys, int argno, cl_object hashtable, cl_object value) cl_index i; i = hashtable->hash.entries + 1; - if (i > 512) { - /* It does not make sense to let these hashes grow large */ - cl_clrhash(hashtable); - } else if (i >= hashtable->hash.size || - i >= (hashtable->hash.size * hashtable->hash.factor)) { - ecl_extend_hashtable(hashtable); + if (i >= hashtable->hash.size || + i >= (hashtable->hash.size * hashtable->hash.factor)) { + if (hashtable->hash.size > 4092) { + /* It does not make sense to let these hashes grow large */ + cl_clrhash(hashtable); + } else { + ecl_extend_hashtable(hashtable); + } } keylist = Cnil; for (p = keys + argno; p > keys; p--) keylist = CONS(p[-1], keylist); @@ -157,10 +210,24 @@ standard_dispatch(cl_narg narg, cl_object gf, cl_object *args) int i, spec_no; struct ecl_hashtable_entry *e; cl_object spec_how_list = GFUN_SPEC(gf); - cl_object table = GFUN_HASH(gf); - cl_object argtype[LAMBDA_PARAMETERS_LIMIT]; + cl_object table = cl_env.gfun_hash; + cl_object argtype[1+LAMBDA_PARAMETERS_LIMIT]; - for (spec_no = 0; spec_how_list != Cnil;) { +#ifdef ECL_THREADS + /* See whether we have to clear the hash from some generic functions right now. */ + if (cl_env.gfun_hash_clear_list != Cnil) { + cl_object clear_list; + THREAD_OP_LOCK(); + clear_list = cl_env.gfun_hash_clear_list; + for ( ; clear_list != Cnil ; clear_list = CDR(clear_list)) { + do_clear_gfun_hash(CAR(clear_list)); + } + cl_env.gfun_hash_clear_list = Cnil; + THREAD_OP_UNLOCK(); + } +#endif + argtype[0] = gf; + for (spec_no = 1; spec_how_list != Cnil;) { cl_object spec_how = CAR(spec_how_list); cl_object spec_type = CAR(spec_how); int spec_position = fix(CDR(spec_how)); diff --git a/src/c/hash.d b/src/c/hash.d index 62b3a9809..3723bd587 100644 --- a/src/c/hash.d +++ b/src/c/hash.d @@ -19,172 +19,7 @@ #include #include #include - -/******************** - * HASHING ROUTINES * - ********************/ - -/* - * SBCL'S old mashing function. Leads to many collisions. - */ - -#if 0 - -#define mash(h,n) ((((h) << 5) | ((h) >> (FIXNUM_BITS - 5))) ^ (n)) -#define hash_word(h,x) mash(h,(cl_index)x) - -static cl_hashkey -hash_string(cl_hashkey h, const unsigned char *buf, cl_index len) -{ - for (; len; len--) { - h = mash(h, (*buf++)); - } - return h; -} - -#endif - -/* - * SBCL's newest algorithm. Leads to few collisions, and it is faster. - */ - -#if FIXNUM_BITS > 32 -/* - * 64 bit version - */ -#define GOLDEN_RATIO 0x9e3779b97f4a7c13L -#define mix(a,b,c) \ - { \ - a=a-b; a=a-c; a=a^(c>>43); \ - b=b-c; b=b-a; b=b^(a<<9); \ - c=c-a; c=c-b; c=c^(b>>8); \ - a=a-b; a=a-c; a=a^(c>>38); \ - b=b-c; b=b-a; b=b^(a<<23); \ - c=c-a; c=c-b; c=c^(b>>5); \ - a=a-b; a=a-c; a=a^(c>>35); \ - b=b-c; b=b-a; b=b^(a<<49); \ - c=c-a; c=c-b; c=c^(b>>11); \ - a=a-b; a=a-c; a=a^(c>>12); \ - b=b-c; b=b-a; b=b^(a<<18); \ - c=c-a; c=c-b; c=c^(b>>22); \ - } - -#define extract_word(k) \ - (k[0]+((cl_index)k[1]<<8)+((cl_index)k[2]<<16)+((cl_index)k[3]<<24)+ \ - ((cl_index)k[4]<<32)+((cl_index)k[5]<<40)+((cl_index)k[6]<<48)+ \ - ((cl_index)k[7]<<52)) - -static cl_index -hash_string(cl_index initval, const unsigned char *k, cl_index len) -{ - register cl_index a = GOLDEN_RATIO, b = GOLDEN_RATIO, c = initval; - for (; len > 24; ) { - a += extract_word(k); k+=8; - b += extract_word(k); k+=8; - c += extract_word(k); k+=8; - mix(a,b,c); - len -= 24; - } - - /*------------------------------------- handle the last 11 bytes */ - c += len; - switch(len) { - /* all the case statements fall through */ - case 23: c+=((cl_index)k[22]<<52); - case 22: c+=((cl_index)k[21]<<48); - case 21: c+=((cl_index)k[20]<<40); - case 20: c+=((cl_index)k[19]<<32); - case 19: c+=((cl_index)k[18]<<24); - case 18: c+=((cl_index)k[17]<<16); - case 17: c+=((cl_index)k[16]<<8); - /* the first byte of c is reserved for the length */ - case 16: b+=((cl_index)k[15]<<52); - case 15: b+=((cl_index)k[14]<<48); - case 14: b+=((cl_index)k[13]<<40); - case 13: b+=((cl_index)k[12]<<32); - case 12: b+=((cl_index)k[11]<<24); - case 11: b+=((cl_index)k[10]<<16); - case 10: b+=((cl_index)k[9]<<8); - case 9 : b+=k[8]; - case 8 : a+=((cl_index)k[7]<<52); - case 7 : a+=((cl_index)k[6]<<48); - case 6 : a+=((cl_index)k[5]<<40); - case 5 : a+=((cl_index)k[4]<<32); - case 4 : a+=((cl_index)k[3]<<24); - case 3 : a+=((cl_index)k[2]<<16); - case 2 : a+=((cl_index)k[1]<<8); - case 1 : a+=k[0]; - /* case 0: nothing left to add */ - } - mix(a,b,c); - /*-------------------------------------------- report the result */ - return c; -} - -#else -/* - * 32 bit version - */ - -#define GOLDEN_RATIO 0x9e3779b9L -#define mix(a,b,c) \ - { \ - a -= b; a -= c; a ^= (c>>13); \ - b -= c; b -= a; b ^= (a<<8); \ - c -= a; c -= b; c ^= (b>>13); \ - a -= b; a -= c; a ^= (c>>12); \ - b -= c; b -= a; b ^= (a<<16); \ - c -= a; c -= b; c ^= (b>>5); \ - a -= b; a -= c; a ^= (c>>3); \ - b -= c; b -= a; b ^= (a<<10); \ - c -= a; c -= b; c ^= (b>>15); \ - } -#define extract_word(k) \ - (k[0]+((cl_index)k[1]<<8)+((cl_index)k[2]<<16)+((cl_index)k[3]<<24)) - - -static cl_index -hash_string(cl_index initval, const unsigned char *k, cl_index len) -{ - register cl_index a = GOLDEN_RATIO, b = GOLDEN_RATIO, c = initval; - for (; len > 12; ) { - a += extract_word(k); k += 4; - b += extract_word(k); k += 4; - c += extract_word(k); k += 4; - mix(a,b,c); - len -= 12; - } - - /*------------------------------------- handle the last 11 bytes */ - c += len; - switch(len) { - /* all the case statements fall through */ - case 11: c+=((cl_index)k[10]<<24); - case 10: c+=((cl_index)k[9]<<16); - case 9 : c+=((cl_index)k[8]<<8); - /* the first byte of c is reserved for the length */ - case 8 : b+=((cl_index)k[7]<<24); - case 7 : b+=((cl_index)k[6]<<16); - case 6 : b+=((cl_index)k[5]<<8); - case 5 : b+=k[4]; - case 4 : a+=((cl_index)k[3]<<24); - case 3 : a+=((cl_index)k[2]<<16); - case 2 : a+=((cl_index)k[1]<<8); - case 1 : a+=k[0]; - /* case 0: nothing left to add */ - } - mix(a,b,c); - /*-------------------------------------------- report the result */ - return c; -} -#endif - -static cl_index hash_word(cl_index c, cl_index a) -{ - cl_index b = GOLDEN_RATIO; - mix(a, b, c); - return c; -} +#include "newhash.h" static void corrupted_hash(cl_object hashtable) /*__attribute__((noreturn))*/; diff --git a/src/c/main.d b/src/c/main.d index 9fad212b3..12849efa3 100644 --- a/src/c/main.d +++ b/src/c/main.d @@ -93,6 +93,17 @@ ecl_init_env(struct cl_env_struct *env) ((struct ecl_fficall*)env->fficall)->registers = 0; #endif +#ifdef CLOS +#ifdef ECL_THREADS + env->gfun_hash_clear_list = Cnil; +#endif + env->gfun_hash = + cl__make_hash_table(@'equalp', MAKE_FIXNUM(1024), /* size */ + ecl_make_singlefloat(1.5f), /* rehash-size */ + ecl_make_singlefloat(0.5f), /* rehash-threshold */ + Cnil); /* thread-safe */ +#endif + init_stacks(&i); } diff --git a/src/c/newhash.h b/src/c/newhash.h new file mode 100644 index 000000000..a10f64f5a --- /dev/null +++ b/src/c/newhash.h @@ -0,0 +1,146 @@ +/******************** + * HASHING ROUTINES * + ********************/ + +/* + * SBCL's newest algorithm. Leads to few collisions, and it is faster. + */ + +#if FIXNUM_BITS > 32 +/* + * 64 bit version + */ +#define GOLDEN_RATIO 0x9e3779b97f4a7c13L +#define mix(a,b,c) \ + { \ + a=a-b; a=a-c; a=a^(c>>43); \ + b=b-c; b=b-a; b=b^(a<<9); \ + c=c-a; c=c-b; c=c^(b>>8); \ + a=a-b; a=a-c; a=a^(c>>38); \ + b=b-c; b=b-a; b=b^(a<<23); \ + c=c-a; c=c-b; c=c^(b>>5); \ + a=a-b; a=a-c; a=a^(c>>35); \ + b=b-c; b=b-a; b=b^(a<<49); \ + c=c-a; c=c-b; c=c^(b>>11); \ + a=a-b; a=a-c; a=a^(c>>12); \ + b=b-c; b=b-a; b=b^(a<<18); \ + c=c-a; c=c-b; c=c^(b>>22); \ + } + +#define extract_word(k) \ + (k[0]+((cl_index)k[1]<<8)+((cl_index)k[2]<<16)+((cl_index)k[3]<<24)+ \ + ((cl_index)k[4]<<32)+((cl_index)k[5]<<40)+((cl_index)k[6]<<48)+ \ + ((cl_index)k[7]<<52)) + +static cl_index +hash_string(cl_index initval, const unsigned char *k, cl_index len) +{ + register cl_index a = GOLDEN_RATIO, b = GOLDEN_RATIO, c = initval; + for (; len > 24; ) { + a += extract_word(k); k+=8; + b += extract_word(k); k+=8; + c += extract_word(k); k+=8; + mix(a,b,c); + len -= 24; + } + + /*------------------------------------- handle the last 11 bytes */ + c += len; + switch(len) { + /* all the case statements fall through */ + case 23: c+=((cl_index)k[22]<<52); + case 22: c+=((cl_index)k[21]<<48); + case 21: c+=((cl_index)k[20]<<40); + case 20: c+=((cl_index)k[19]<<32); + case 19: c+=((cl_index)k[18]<<24); + case 18: c+=((cl_index)k[17]<<16); + case 17: c+=((cl_index)k[16]<<8); + /* the first byte of c is reserved for the length */ + case 16: b+=((cl_index)k[15]<<52); + case 15: b+=((cl_index)k[14]<<48); + case 14: b+=((cl_index)k[13]<<40); + case 13: b+=((cl_index)k[12]<<32); + case 12: b+=((cl_index)k[11]<<24); + case 11: b+=((cl_index)k[10]<<16); + case 10: b+=((cl_index)k[9]<<8); + case 9 : b+=k[8]; + case 8 : a+=((cl_index)k[7]<<52); + case 7 : a+=((cl_index)k[6]<<48); + case 6 : a+=((cl_index)k[5]<<40); + case 5 : a+=((cl_index)k[4]<<32); + case 4 : a+=((cl_index)k[3]<<24); + case 3 : a+=((cl_index)k[2]<<16); + case 2 : a+=((cl_index)k[1]<<8); + case 1 : a+=k[0]; + /* case 0: nothing left to add */ + } + mix(a,b,c); + /*-------------------------------------------- report the result */ + return c; +} + +#else +/* + * 32 bit version + */ + +#define GOLDEN_RATIO 0x9e3779b9L +#define mix(a,b,c) \ + { \ + a -= b; a -= c; a ^= (c>>13); \ + b -= c; b -= a; b ^= (a<<8); \ + c -= a; c -= b; c ^= (b>>13); \ + a -= b; a -= c; a ^= (c>>12); \ + b -= c; b -= a; b ^= (a<<16); \ + c -= a; c -= b; c ^= (b>>5); \ + a -= b; a -= c; a ^= (c>>3); \ + b -= c; b -= a; b ^= (a<<10); \ + c -= a; c -= b; c ^= (b>>15); \ + } +#define extract_word(k) \ + (k[0]+((cl_index)k[1]<<8)+((cl_index)k[2]<<16)+((cl_index)k[3]<<24)) + + +static cl_index +hash_string(cl_index initval, const unsigned char *k, cl_index len) +{ + register cl_index a = GOLDEN_RATIO, b = GOLDEN_RATIO, c = initval; + for (; len > 12; ) { + a += extract_word(k); k += 4; + b += extract_word(k); k += 4; + c += extract_word(k); k += 4; + mix(a,b,c); + len -= 12; + } + + /*------------------------------------- handle the last 11 bytes */ + c += len; + switch(len) { + /* all the case statements fall through */ + case 11: c+=((cl_index)k[10]<<24); + case 10: c+=((cl_index)k[9]<<16); + case 9 : c+=((cl_index)k[8]<<8); + /* the first byte of c is reserved for the length */ + case 8 : b+=((cl_index)k[7]<<24); + case 7 : b+=((cl_index)k[6]<<16); + case 6 : b+=((cl_index)k[5]<<8); + case 5 : b+=k[4]; + case 4 : a+=((cl_index)k[3]<<24); + case 3 : a+=((cl_index)k[2]<<16); + case 2 : a+=((cl_index)k[1]<<8); + case 1 : a+=k[0]; + /* case 0: nothing left to add */ + } + mix(a,b,c); + /*-------------------------------------------- report the result */ + return c; +} +#endif + +static cl_index hash_word(cl_index c, cl_index a) +{ + cl_index b = GOLDEN_RATIO; + mix(a, b, c); + return c; +} + diff --git a/src/c/symbols_list.h b/src/c/symbols_list.h index 11f80ba09..7cc9fb469 100644 --- a/src/c/symbols_list.h +++ b/src/c/symbols_list.h @@ -1673,6 +1673,10 @@ cl_symbols[] = { {SYS_ "CODE-BLOCK", SI_ORDINARY, NULL, -1, OBJNULL}, +#ifdef CLOS +{SYS_ "CLEAR-GFUN-HASH", SI_ORDINARY, si_clear_gfun_hash, 1, OBJNULL}, +#endif + /* Tag for end of list */ {NULL, CL_ORDINARY, NULL, -1, OBJNULL}}; diff --git a/src/c/symbols_list2.h b/src/c/symbols_list2.h index 81995ac37..8f953507d 100644 --- a/src/c/symbols_list2.h +++ b/src/c/symbols_list2.h @@ -1673,6 +1673,10 @@ cl_symbols[] = { {SYS_ "CODE-BLOCK",NULL}, +#ifdef CLOS +{SYS_ "CLEAR-GFUN-HASH","si_clear_gfun_hash"}, +#endif + /* Tag for end of list */ {NULL,NULL}}; diff --git a/src/clos/fixup.lsp b/src/clos/fixup.lsp index e52462edd..7762e7852 100644 --- a/src/clos/fixup.lsp +++ b/src/clos/fixup.lsp @@ -189,7 +189,7 @@ their lambda lists ~A and ~A are not congruent." (setf (generic-function-methods gf) (delete method (generic-function-methods gf)) (method-generic-function method) nil) - (clrhash (generic-function-method-hash gf)) + (si:clear-gfun-hash gf) gf) ;;; ---------------------------------------------------------------------- diff --git a/src/clos/kernel.lsp b/src/clos/kernel.lsp index fa66bb1e5..0437ebebd 100644 --- a/src/clos/kernel.lsp +++ b/src/clos/kernel.lsp @@ -86,13 +86,6 @@ (defparameter +standard-generic-function-slots+ '((name :initarg :name :initform nil :accessor generic-function-name) - (method-hash :accessor generic-function-method-hash - :initform (make-hash-table - :test #'eql - ;; use fixnums as limits for efficiency: - :size *default-method-cache-size* - :rehash-size #.(/ *default-method-cache-size* 2) - :rehash-threshold 0.5f0)) (spec-list :initform nil :accessor generic-function-spec-list) (method-combination :initarg :method-combination :initform '(standard) @@ -202,13 +195,7 @@ (fdefinition name) ;; create a fake standard-generic-function object: (let ((gfun (si:allocate-raw-instance nil (find-class 't) - #.(length +standard-generic-function-slots+))) - (hash (make-hash-table - :test #'eql - ;; use fixnums as limits for efficiency: - :size *default-method-cache-size* - :rehash-size #.(/ *default-method-cache-size* 2) - :rehash-threshold 0.5f0))) + #.(length +standard-generic-function-slots+)))) (declare (type standard-object gfun)) ;; create a new gfun (si::instance-sig-set gfun) @@ -216,8 +203,7 @@ (generic-function-lambda-list gfun) lambda-list (generic-function-method-combination gfun) '(standard) (generic-function-methods gfun) nil - (generic-function-spec-list gfun) nil - (generic-function-method-hash gfun) hash) + (generic-function-spec-list gfun) nil) (when l-l-p (setf (generic-function-argument-precedence-order gfun) (rest (si::process-lambda-list lambda-list t)))) @@ -367,7 +353,7 @@ (list ,@a-p-o))) 'function)))))) (setf (generic-function-a-p-o-function gf) function) - (clrhash (generic-function-method-hash gf))))) + (si:clear-gfun-hash gf)))) (defun print-object (object stream) (print-unreadable-object (object stream))) diff --git a/src/h/external.h b/src/h/external.h index 703baa958..7579ce219 100644 --- a/src/h/external.h +++ b/src/h/external.h @@ -97,6 +97,16 @@ struct cl_env_struct { #endif int interrupt_pending; + /* The following is a hash table for caching invocations of + generic functions. In a multithreaded environment we must + queue operations in which the hash is cleared from updated + generic functions. */ +#ifdef CLOS + cl_object gfun_hash; +#ifdef ECL_THREADS + cl_object gfun_hash_clear_list; +#endif +#endif /* foreign function interface */ void *fficall; }; @@ -630,6 +640,7 @@ extern void ecl_register_root(cl_object *p); /* gfun.c */ #ifdef CLOS +extern cl_object si_clear_gfun_hash(cl_object what); extern cl_object clos_set_funcallable_instance_function(cl_object x, cl_object function_or_t); extern cl_object si_generic_function_p(cl_object instance); diff --git a/src/h/internal.h b/src/h/internal.h index c71f3a1ad..f7cd58e86 100644 --- a/src/h/internal.h +++ b/src/h/internal.h @@ -165,9 +165,8 @@ extern void ecl_extend_hashtable(cl_object hashtable); /* gfun.d, kernel.lsp */ #define GFUN_NAME(x) ((x)->instance.slots[0]) -#define GFUN_HASH(x) ((x)->instance.slots[1]) -#define GFUN_SPEC(x) ((x)->instance.slots[2]) -#define GFUN_COMB(x) ((x)->instance.slots[3]) +#define GFUN_SPEC(x) ((x)->instance.slots[1]) +#define GFUN_COMB(x) ((x)->instance.slots[2]) /* package.d */ diff --git a/src/lsp/config.lsp.in b/src/lsp/config.lsp.in index 85e079c0d..fe476228c 100644 --- a/src/lsp/config.lsp.in +++ b/src/lsp/config.lsp.in @@ -24,7 +24,7 @@ Returns, as a string, the location of the machine on which ECL runs." (defun lisp-implementation-version () "Args:() Returns the version of your ECL as a string." - "@PACKAGE_VERSION@ (CVS 2008-01-05 22:02)") + "@PACKAGE_VERSION@ (CVS 2008-01-06 16:18)") (defun machine-type () "Args: ()