From c2628d43b72d22ca4c363defc8055733e09fa708 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Fri, 18 Feb 2011 20:08:26 +0100 Subject: [PATCH 1/7] Clean up A* sector cache leftovers The sector cache was disabled in v4.2.2, and dropped in commit 8f40f5ad, v4.2.20. A bit of cache statistics code was left behind. Remove it. --- src/lib/common/path.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/lib/common/path.c b/src/lib/common/path.c index 6313a617..cbd5f0a6 100644 --- a/src/lib/common/path.c +++ b/src/lib/common/path.c @@ -51,8 +51,6 @@ #define BP_NEIGHBORS 6 /* max number of neighbors */ struct bestp { - int sctcache_hits; - int sctcache_misses; int bp_mobtype; struct as_data *adp; }; @@ -123,11 +121,6 @@ best_path(struct sctstr *from, struct sctstr *to, char *path, int mob_type) #ifdef AS_STATS as_stats(adp, stderr); #endif /* AS_STATS */ -#ifdef BP_STATS - fprintf(stderr, "best path %s\n", path); - fprintf(stderr, "cache hits/misses: %d/%d\n", - bp->sctcache_hits, bp->sctcache_misses); -#endif /* BP_STATS */ return 0; } From 8f92fe40f44bb47e42e1e1c2f8456d1f8bb05b9b Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Sat, 19 Feb 2011 10:47:32 +0100 Subject: [PATCH 2/7] More precise and complete A* performance statistics Memory usage didn't include path (adp->path), neighbor cache (adp->neighbor_coords, adp->neighbor_nodes), and the hash table (adp->hashtab). While there, print path length. To get A* statistics on stderr, compile with AS_STATS defined. --- src/lib/as/as_stats.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/lib/as/as_stats.c b/src/lib/as/as_stats.c index cadc8dc4..ed8d4885 100644 --- a/src/lib/as/as_stats.c +++ b/src/lib/as/as_stats.c @@ -31,8 +31,11 @@ as_stats(struct as_data *adp, FILE * fp) int i; int j; int total_q; + int total_p; int total_h; + size_t other; struct as_queue *qp; + struct as_path *pp; struct as_hash *hp; fprintf(fp, "Statistics:\n"); @@ -52,6 +55,10 @@ as_stats(struct as_data *adp, FILE * fp) i++; fprintf(fp, "\tsubsumed:\t%d\n", i); total_q += i; + for (i = 0, pp = adp->path; pp; pp = pp->next) + i++; + total_p = i; + fprintf(fp, "path length: %d\n", total_p); fprintf(fp, "hash table statistics (size %d):\n", adp->hashsize); for (i = 0; i < adp->hashsize; i++) { for (j = 0, hp = adp->hashtab[i]; hp; hp = hp->next) @@ -64,10 +71,18 @@ as_stats(struct as_data *adp, FILE * fp) fprintf(fp, "\tqueues\t%d\n", (int)(total_q * sizeof(struct as_queue))); fprintf(fp, "\tnodes\t%d\n", (int)(total_q * sizeof(struct as_node))); + fprintf(fp, "\tpath\t%d\n", (int)(total_p * sizeof(struct as_path))); fprintf(fp, "\thash ents\t%d\n", (int)(total_h * sizeof(struct as_hash))); + other = sizeof(struct as_data); + other += adp->maxneighbors * sizeof(struct as_coord); + other += (adp->maxneighbors + 1) * sizeof(struct as_node *); + other += adp->hashsize * sizeof(struct as_hash *); + fprintf(fp, "\tother\t%d\n", (int)other); fprintf(fp, "\ttotal\t%d\n", (int)(total_q * sizeof(struct as_queue) + total_q * sizeof(struct as_node) + - total_h * sizeof(struct as_hash))); + total_p * sizeof(struct as_path) + + total_h * sizeof(struct as_hash) + + other)); } From 2797e58c20fb60a286cb016540e0dc1a190e5356 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Sat, 19 Feb 2011 13:19:27 +0100 Subject: [PATCH 3/7] A* path and neighbor cache performance statistics as_clear_cachepath() now prints cache hits, misses, number of entries, and memory use to stderr, when compiled with AS_STATS defined. --- src/lib/as/as_cache.c | 42 +++++++++++++++++++++++++++++++++++++++++- src/lib/common/path.c | 2 ++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/src/lib/as/as_cache.c b/src/lib/as/as_cache.c index d98e088e..eff08c31 100644 --- a/src/lib/as/as_cache.c +++ b/src/lib/as/as_cache.c @@ -58,6 +58,15 @@ static struct as_frompath **fromhead = (struct as_frompath **)0; static int as_cachepath_on = 0; /* Default to off */ +#ifdef AS_STATS +unsigned as_cache_tries, as_cache_hits; +#define as_cache_try() ((void)as_cache_tries++) +#define as_cache_hit() ((void)as_cache_hits++) +#else +#define as_cache_try() ((void)0) +#define as_cache_hit() ((void)0) +#endif + void as_enable_cachepath(void) { @@ -142,6 +151,13 @@ as_clear_cachepath(void) struct as_frompath *from, *from2; struct as_topath *to, *to2; int i, j; +#ifdef AS_STATS + size_t index_sz = 0; + unsigned index_nb = 0, paths_nb = 0, paths = 0; +#define stats_index(sz) ((void)(index_sz += (sz), index_nb++)) +#else +#define stats_index(sz) ((void)0) +#endif /* Cache not used yet :) */ if (fromhead == NULL) @@ -153,22 +169,43 @@ as_clear_cachepath(void) for (to = from->tolist[i]; to; to = to2) { to2 = to->next; /* Free this path */ +#ifdef AS_STATS + { + struct as_path *pp; + for (pp = to->path; pp; pp = pp->next) + paths_nb++; + paths++; + } +#endif as_free_path(to->path); /* Free this node */ free(to); + stats_index(sizeof(*to)); } } /* Now, free the list of lists */ free(from->tolist); + stats_index(WORLD_Y * sizeof(*from->tolist)); /* Save the next pointer */ from2 = from->next; /* now, free this from node */ free(from); + stats_index(sizeof(*from)); } } /* Note we don't free the fromhead here, we just zero it. That way, we can use it next time without mallocing int */ memset(fromhead, 0, (sizeof(struct as_frompath *) * WORLD_Y)); + stats_index(WORLD_Y * sizeof(*fromhead)); +#ifdef AS_STATS + fprintf(stderr, "as_cache %u searches, %u hits, %u entries," + " index %zu bytes %u blocks, paths %zu bytes %u blocks\n", + as_cache_tries, as_cache_hits, + paths, + index_sz, index_nb, + paths_nb * sizeof(struct as_path), paths_nb); + as_cache_hits = as_cache_tries = 0; +#endif } struct as_path * @@ -177,6 +214,7 @@ as_find_cachepath(coord fx, coord fy, coord tx, coord ty) struct as_frompath *from; struct as_topath *to; + as_cache_try(); /* Is the cache on? if not, return NULL */ if (as_cachepath_on == 0) return NULL; @@ -189,8 +227,10 @@ as_find_cachepath(coord fx, coord fy, coord tx, coord ty) for (from = fromhead[fy]; from; from = from->next) { if (from->x == fx) { for (to = from->tolist[ty]; to; to = to->next) { - if (to->x == tx) + if (to->x == tx) { + as_cache_hit(); return to->path; + } } } } diff --git a/src/lib/common/path.c b/src/lib/common/path.c index cbd5f0a6..965da6e2 100644 --- a/src/lib/common/path.c +++ b/src/lib/common/path.c @@ -120,6 +120,8 @@ best_path(struct sctstr *from, struct sctstr *to, char *path, int mob_type) #ifdef AS_STATS as_stats(adp, stderr); + fprintf(stderr, "neighbor cache %zu bytes\n", + WORLD_SZ() * 6 * sizeof(struct sctstr *)); #endif /* AS_STATS */ return 0; } From 0385c67a8fce0c17c341364cc340428de08c8b45 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Sat, 19 Feb 2011 13:50:10 +0100 Subject: [PATCH 4/7] Fix when best_path() prints A* performance statistics Print them when A* actually runs, not when best_path() finds a path. Statistics for unsuccessful runs were lost, and old statistics were printed for path cache hits. --- src/lib/common/path.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/lib/common/path.c b/src/lib/common/path.c index 965da6e2..9cdf959e 100644 --- a/src/lib/common/path.c +++ b/src/lib/common/path.c @@ -98,6 +98,7 @@ best_path(struct sctstr *from, struct sctstr *to, char *path, int mob_type) static struct bestp *mybestpath; struct as_data *adp; struct as_path *ap; + int res; if (!mybestpath) mybestpath = bp_init(); @@ -110,19 +111,19 @@ best_path(struct sctstr *from, struct sctstr *to, char *path, int mob_type) adp->to.y = to->sct_y; mybestpath->bp_mobtype = mob_type; - if (as_search(adp) < 0) + res = as_search(adp); +#ifdef AS_STATS + as_stats(adp, stderr); + fprintf(stderr, "neighbor cache %zu bytes\n", + WORLD_SZ() * 6 * sizeof(struct sctstr *)); +#endif + if (res < 0) return -1; ap = adp->path; } if (bp_path(ap, path) < 0) return -1; - -#ifdef AS_STATS - as_stats(adp, stderr); - fprintf(stderr, "neighbor cache %zu bytes\n", - WORLD_SZ() * 6 * sizeof(struct sctstr *)); -#endif /* AS_STATS */ return 0; } From a02d3e9fc17b17af720f60c5dbd0639c8858481d Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Sat, 19 Feb 2011 15:00:03 +0100 Subject: [PATCH 5/7] Permit disabling of A* path cache at compile-time Mostly to measure its effectiveness. Compile with AS_NO_PATH_CACHE defined to disable it. Turns out the path cache is quite effective. For my continental test case (Hvy Metal 2 updates), it reduces the number of searches by a factor of 18.5, speeding up distribution path assembly by a factor of 7. The price is memory: it uses 135 times more memory than the A* library. For my island test case (Hvy Plastic 2 updates), I get 4 times search reduction, 3.5 times faster distribution path assembly, 36 times more memory. --- src/lib/common/path.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/lib/common/path.c b/src/lib/common/path.c index 9cdf959e..f36d4828 100644 --- a/src/lib/common/path.c +++ b/src/lib/common/path.c @@ -26,7 +26,6 @@ * --- * * path.c: Empire/A* Interface code. - * Define AS_STATS for A* statistics. * * Known contributors to this file: * Phil Lapsley, 1991 @@ -35,6 +34,14 @@ * Steve McClure, 1997 */ +/* + * Define AS_STATS for A* statistics on stderr. + * + * Define AS_NO_PATH_CACHE to disable the path cache. The path cache + * saves a lot of work, but uses lots of memory. It should be a + * significant net win, unless you run out of memory. + */ + #include #include @@ -103,7 +110,11 @@ best_path(struct sctstr *from, struct sctstr *to, char *path, int mob_type) if (!mybestpath) mybestpath = bp_init(); adp = mybestpath->adp; +#ifdef AS_NO_PATH_CACHE + ap = NULL; +#else ap = as_find_cachepath(from->sct_x, from->sct_y, to->sct_x, to->sct_y); +#endif if (ap == NULL) { adp->from.x = from->sct_x; adp->from.y = from->sct_y; @@ -278,19 +289,25 @@ bp_coord_hash(struct as_coord c) void bp_enable_cachepath(void) { +#ifndef AS_NO_PATH_CACHE as_enable_cachepath(); +#endif } void bp_disable_cachepath(void) { +#ifndef AS_NO_PATH_CACHE as_disable_cachepath(); +#endif } void bp_clear_cachepath(void) { +#ifndef AS_NO_PATH_CACHE as_clear_cachepath(); +#endif } double From 7edcd3ea771f1894f0cf06d0057328837b4cfd17 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Sun, 20 Feb 2011 08:43:44 +0100 Subject: [PATCH 6/7] Permit disabling of A* neighbor cache at compile-time Mostly to measure its effectiveness. Compile with AS_NO_NEIGHBOR_CACHE defined to disable it. The neighbor cache turns out to be useless in my tests: it eats memory without speeding up the update's distribution path assembly. --- src/lib/common/path.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/lib/common/path.c b/src/lib/common/path.c index f36d4828..58fded85 100644 --- a/src/lib/common/path.c +++ b/src/lib/common/path.c @@ -40,6 +40,10 @@ * Define AS_NO_PATH_CACHE to disable the path cache. The path cache * saves a lot of work, but uses lots of memory. It should be a * significant net win, unless you run out of memory. + * + * Define AS_NO_NEIGHBOR_CACHE to disable the neighbor cache. The + * neighbor cache trades a modest amount of memory to save a bit of + * work. In its current form, it doesn't really make a difference. */ #include @@ -87,8 +91,10 @@ bp_init(void) if (bp->adp == NULL) return NULL; +#ifndef AS_NO_NEIGHBOR_CACHE if (neighsects == NULL) neighsects = calloc(WORLD_SZ() * 6, sizeof(struct sctstr *)); +#endif return bp; } @@ -125,8 +131,10 @@ best_path(struct sctstr *from, struct sctstr *to, char *path, int mob_type) res = as_search(adp); #ifdef AS_STATS as_stats(adp, stderr); +#ifndef AS_NO_NEIGHBOR_CACHE fprintf(stderr, "neighbor cache %zu bytes\n", WORLD_SZ() * 6 * sizeof(struct sctstr *)); +#endif #endif if (res < 0) return -1; From e8825678899e4407214be76dc2117d745e3f70e2 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 24 Feb 2011 19:29:33 +0100 Subject: [PATCH 7/7] Speed up A* neighbor cache hits struct sctstr members sct_x, sct_y are normalized, no need to normalize them again. The neighbor cache now speeds up distribution path assembly by about 10% without the path cache, and by about 5% with the path cache. --- src/lib/common/path.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/common/path.c b/src/lib/common/path.c index 58fded85..7350e309 100644 --- a/src/lib/common/path.c +++ b/src/lib/common/path.c @@ -227,8 +227,8 @@ bp_neighbors(struct as_coord c, struct as_coord *cp, void *pp) *ssp = sp; } else { sp = *ssp; - sx = XNORM(sp->sct_x); - sy = YNORM(sp->sct_y); + sx = sp->sct_x; + sy = sp->sct_y; } /* No need to calculate cost each time, just make sure we can move through it. We calculate it later. */