From 809691aa3dc370423bd71b10b7a77727c48a380b Mon Sep 17 00:00:00 2001 From: Heikki Hannikainen Date: Thu, 15 Nov 2012 22:05:14 +0200 Subject: [PATCH] Slightly optimize the most frequently called function, heard_find() Moved repeated key hash calculation out to the filter preprocess phase. Moved heard list expiration to be done once per 2 minutes, instead of every time. 32 hash buckets instead of 16. Less function arguments. --- src/accept.c | 2 + src/client_heard.c | 129 ++++++++++++++++++++++++++++----------------- src/client_heard.h | 5 +- src/filter.c | 21 ++++++-- src/worker.c | 9 ++++ src/worker.h | 10 ++-- 6 files changed, 120 insertions(+), 56 deletions(-) diff --git a/src/accept.c b/src/accept.c index 5697e15..557d80d 100644 --- a/src/accept.c +++ b/src/accept.c @@ -1084,6 +1084,8 @@ static int accept_liveupgrade_single(cJSON *client, int *rxerr_map, int rxerr_ma /* distribute keepalive intervals for the existing old clients * but send them rather sooner than later */ c->keepalive = tick + (random() % (keepalive_interval/2)); + /* distribute cleanup intervals over the next 2 minutes */ + c->cleanup = tick + (random() % 120); c->connect_time = t_connect->valueint; c->validated = verified->valueint; diff --git a/src/client_heard.c b/src/client_heard.c index 5c968ba..c7941a2 100644 --- a/src/client_heard.c +++ b/src/client_heard.c @@ -65,11 +65,11 @@ static void heard_list_update(struct client_t *c, char *call, int call_len, time idx = hash; // "CLIENT_HEARD_BUCKETS" is 16.. idx ^= (idx >> 16); - idx ^= (idx >> 8); - idx ^= (idx >> 4); + //idx ^= (idx >> 8); + //idx ^= (idx >> 4); i = idx % CLIENT_HEARD_BUCKETS; - //DLOG(LOG_DEBUG, "heard_list_update fd %d %s: updating heard table for %.*s (hash %u i %d)", c->fd, which, call_len, pcall, hash, i); + DLOG(LOG_DEBUG, "heard_list_update fd %d %s: updating %.*s (hash %u i %d)", c->fd, which, call_len, call, hash, i); for (h = list[i]; (h); h = h->next) { if (h->hash == hash && call_len == h->call_len @@ -137,52 +137,47 @@ void client_courtesy_update(struct client_t *c, struct pbuf_t *pb) heard_list_update(c, pb->data, pb->srccall_end - pb->data, pb->t, c->client_courtesy, &c->client_courtesy_count, "courtesy"); } -static int heard_find(struct client_t *c, struct client_heard_t **list, int *entrycount, const char *callsign, int call_len, int storetime, int drop_if_found, char *which) +/* + * Check for and optionally remove a callsign from a heard list. + * + * NOTE: THIS IS THE MOST FREQUENTLY CALLED FUNCTION IN THE APPLICATION. + * + * It's called more than once per packet per filtered client, + * currently about 1.24 * number-of-filtered-clients * packets. + * Not very scalable, as the number of packets goes up with the amount of + * clients on the network, O(N^2) complexity. + * A single tree keyed by the callsign and containing a list of clients + * having heard the callsign, looked up once per packet, would be O(N). + * For the time being this will work fine. + * + * Do not bloat. + */ + +static int heard_find(struct client_heard_t **list, int *entrycount, const char *callsign, int call_len, uint32_t hash, int drop_if_found) { - struct client_heard_t *h, *next; - uint32_t hash, idx; - int i; + struct client_heard_t *h; + uint32_t idx; - hash = keyhashuc(callsign, call_len, 0); - idx = hash; // "CLIENT_HEARD_BUCKETS" is 16.. + /* + idx = hash; idx ^= (idx >> 16); - idx ^= (idx >> 8); - idx ^= (idx >> 4); - i = idx % CLIENT_HEARD_BUCKETS; + //idx ^= (idx >> 8); + //idx ^= (idx >> 4); + idx = idx % CLIENT_HEARD_BUCKETS; + */ + idx = (hash ^ (hash >> 16)) % CLIENT_HEARD_BUCKETS; - //DLOG(LOG_DEBUG, "heard_find fd %d %s: checking for %.*s (hash %u i %d)", c->fd, which, call_len, callsign, hash, i); + //DLOG(LOG_DEBUG, "heard_find fd %d %s: checking for %.*s (hash %u idx %d)", c->fd, which, call_len, callsign, hash, idx); - int expire_below = tick - storetime; - next = NULL; - - for (h = list[i]; (h); h = next) { - next = h->next; // we might free this one - - // expire old entries - // TODO: move expiration to heard_list_update, too, it's run much more often. - if (h->last_heard < expire_below || h->last_heard > tick) { - DLOG(LOG_DEBUG, "heard_find fd %d %s: expiring %.*s (%ul below %ul)", c->fd, which, h->call_len, h->callsign, h->last_heard, expire_below); - - *h->prevp = h->next; - if (h->next) - h->next->prevp = h->prevp; -#ifndef _FOR_VALGRIND_ - cellfree(client_heard_cells, h); -#else - hfree(h); -#endif - *entrycount = *entrycount -1; - continue; - } - + for (h = list[idx]; (h); h = h->next) { if (h->hash == hash && call_len == h->call_len && strncasecmp(callsign, h->callsign, h->call_len) == 0) { /* OK, found it from the list. */ - //DLOG(LOG_DEBUG, "heard_find fd %d %s: found %.*s%s", c->fd, which, call_len, callsign, (drop_if_found) ? " (dropping)" : ""); + //DLOG(LOG_DEBUG, "heard_find: found %.*s%s", call_len, callsign, (drop_if_found) ? " (dropping)" : ""); if (drop_if_found) { - DLOG(LOG_DEBUG, "heard_find fd %d %s: dropping %.*s%s", c->fd, which, call_len, callsign, (drop_if_found) ? " (dropping)" : ""); + //DLOG(LOG_DEBUG, "heard_find: dropping %.*s%s", call_len, callsign, (drop_if_found) ? " (dropping)" : ""); *h->prevp = h->next; if (h->next) h->next->prevp = h->prevp; @@ -202,18 +197,18 @@ static int heard_find(struct client_t *c, struct client_heard_t **list, int *ent return 0; } -int client_heard_check(struct client_t *c, const char *callsign, int call_len) +int client_heard_check(struct client_t *c, const char *callsign, int call_len, uint32_t hash) { - return heard_find(c, c->client_heard, &c->client_heard_count, - callsign, call_len, - heard_list_storetime, 0, "heard"); + return heard_find(c->client_heard, &c->client_heard_count, + callsign, call_len, hash, + 0); } -int client_courtesy_needed(struct client_t *c, const char *callsign, int call_len) +int client_courtesy_needed(struct client_t *c, struct pbuf_t *pb) { - return heard_find(c, c->client_courtesy, &c->client_courtesy_count, - callsign, call_len, - courtesy_list_storetime, 1, "courtesy"); + return heard_find(c->client_courtesy, &c->client_courtesy_count, + pb->srcname, pb->srcname_len, pb->srcname_hash, + 1); } /* @@ -249,12 +244,52 @@ void client_heard_free(struct client_t *c) c->client_courtesy_count = 0; } +/* + * Expire old entries from client_heard list + */ + +static void heard_expire_single(struct client_heard_t **list, int *entrycount, int storetime) +{ + struct client_heard_t *n, *h; + int expire_below = tick - storetime; + int i; + + for (i = 0; i < CLIENT_HEARD_BUCKETS; i++) { + h = list[i]; + while (h) { + n = h->next; + if (h->last_heard < expire_below || h->last_heard > tick) { + DLOG(LOG_DEBUG, "heard_expire_single: expiring %.*s (%ul below %ul)", h->call_len, h->callsign, h->last_heard, expire_below); + + *h->prevp = h->next; + if (h->next) + h->next->prevp = h->prevp; +#ifndef _FOR_VALGRIND_ + cellfree(client_heard_cells, h); +#else + hfree(h); +#endif + *entrycount = *entrycount -1; + } + h = n; + } + } +} + +void client_heard_expire(struct client_t *c) +{ + heard_expire_single(c->client_heard, &c->client_heard_count, heard_list_storetime); + heard_expire_single(c->client_courtesy, &c->client_courtesy_count, courtesy_list_storetime); +} + + void client_heard_init(void) { #ifndef _FOR_VALGRIND_ client_heard_cells = cellinit( "client_heard", sizeof(struct client_heard_t), - __alignof__(struct client_heard_t), CELLMALLOC_POLICY_FIFO, + __alignof__(struct client_heard_t), + CELLMALLOC_POLICY_FIFO, 512 /* 512 KB at the time */, 0 /* minfree */ ); /* 512 KB arena size -> about 18k entries per single arena */ #endif diff --git a/src/client_heard.h b/src/client_heard.h index 97bd2dd..09b8dd4 100644 --- a/src/client_heard.h +++ b/src/client_heard.h @@ -17,9 +17,10 @@ extern void client_heard_update(struct client_t *c, struct pbuf_t *pb); extern void client_courtesy_update(struct client_t *c, struct pbuf_t *pb); -extern int client_heard_check(struct client_t *c, const char *callsign, int call_len); -extern int client_courtesy_needed(struct client_t *c, const char *callsign, int call_len); +extern int client_heard_check(struct client_t *c, const char *callsign, int call_len, uint32_t hash); +extern int client_courtesy_needed(struct client_t *c, struct pbuf_t *pb); +extern void client_heard_expire(struct client_t *c); extern void client_heard_free(struct client_t *c); extern void client_heard_init(void); diff --git a/src/filter.c b/src/filter.c index b65ab2e..50a03cc 100644 --- a/src/filter.c +++ b/src/filter.c @@ -786,11 +786,25 @@ static void filter_check_tcpip(struct pbuf_t *pbuf) pbuf->flags |= F_HAS_TCPIP; } +static void filter_keyhashes(struct pbuf_t *pb) +{ + pb->srccall_hash = keyhashuc(pb->data, pb->srccall_end - pb->data, 0); + + if (pb->srcname == pb->data) + pb->srcname_hash = pb->srccall_hash; + else + pb->srcname_hash = keyhashuc(pb->srcname, pb->srcname_len, 0); + + if (pb->dstname) + pb->dstname_hash = keyhashuc(pb->dstname, pb->dstname_len, 0); +} + void filter_preprocess_dupefilter(struct pbuf_t *pbuf) { filter_check_tcpip(pbuf); filter_entrycall_insert(pbuf); filter_wx_insert(pbuf); + filter_keyhashes(pbuf); } void filter_postprocess_dupefilter(struct pbuf_t *pbuf) @@ -2407,7 +2421,7 @@ int filter_process(struct worker_t *self, struct client_t *c, struct pbuf_t *pb) if (pb->packettype & T_MESSAGE) { if ( (pb->dstname_len == c->username_len && memcmp(pb->dstname, c->username, c->username_len) == 0) - || (client_heard_check(c, pb->dstname, pb->dstname_len)) + || (client_heard_check(c, pb->dstname, pb->dstname_len, pb->dstname_hash)) ) { /* insert the source callsign to the courtesy position list */ client_courtesy_update(c, pb); @@ -2419,17 +2433,16 @@ int filter_process(struct worker_t *self, struct client_t *c, struct pbuf_t *pb) * single position packet, too. */ if (pb->packettype & (T_POSITION|T_OBJECT|T_ITEM) - && client_courtesy_needed(c, pb->srcname, pb->srcname_len)) { + && client_courtesy_needed(c, pb)) { FILTER_CLIENT_DEBUG(self, c, "# courtesy position after message\r\n", NULL); return 1; - } /* If the source callsign of a packet having TCPIP* in the path has been * recently heard on this socket, do pass on the packets to this socket too. * This lets igates know that the station is also available on the Internet, * and no TX igating to RF should be done. */ - if ((pb->flags & F_HAS_TCPIP) && client_heard_check(c, pb->data, pb->srccall_end - pb->data)) { + if ((pb->flags & F_HAS_TCPIP) && client_heard_check(c, pb->data, pb->srccall_end - pb->data, pb->srccall_hash)) { FILTER_CLIENT_DEBUG(self, c, "# igate support TCPIP* packet from a heard station\r\n", NULL); return 1; } diff --git a/src/worker.c b/src/worker.c index 1987b66..aa8288d 100644 --- a/src/worker.c +++ b/src/worker.c @@ -383,6 +383,8 @@ struct client_t *client_alloc(void) c->obuf = hmalloc(c->obuf_size); #endif + c->cleanup = tick + 120; + return c; } @@ -1447,6 +1449,13 @@ static void send_keepalives(struct worker_t *self) if ( c->state == CSTATE_COREPEER ) continue; + /* is it time to clean up? */ + if (c->cleanup <= tick || c->cleanup > tick + 120+120) { + c->cleanup = tick + 120; + hlog(LOG_DEBUG, "%s/%s: client cleanup", c->addr_rem, c->username); + client_heard_expire(c); + } + /* Is it time for keepalive? Also send a keepalive if clock jumped backwards. */ if ((c->keepalive <= tick && c->obuf_wtime < w_keepalive) || (c->keepalive > tick + keepalive_interval)) { diff --git a/src/worker.h b/src/worker.h index 9e840f7..4210b09 100644 --- a/src/worker.h +++ b/src/worker.h @@ -105,14 +105,17 @@ struct pbuf_t { is ignored while history dumping is being done. */ - time_t t; /* when the packet was received */ - uint32_t seqnum; /* ever increasing counter, dupecheck sets */ + uint32_t srcname_hash; /* source name hash */ + uint32_t srccall_hash; /* srccall hash */ + uint32_t dstname_hash; /* srccall hash */ uint16_t packettype; /* bitmask: one or more of T_* */ uint16_t flags; /* bitmask: one or more of F_* */ uint16_t srcname_len; /* parsed length of source (object, item, srcall) name 3..9 */ uint16_t dstcall_len; /* parsed length of destination callsign *including* SSID */ uint16_t dstname_len; /* parsed length of message destination including SSID */ uint16_t entrycall_len; + uint32_t seqnum; /* ever increasing counter, dupecheck sets */ + time_t t; /* when the packet was received */ int packet_len; /* the actual length of the packet, including CRLF */ int buf_len; /* the length of this buffer */ @@ -162,7 +165,7 @@ union sockaddr_u { }; /* list of message recipient callsigns heard on a client port */ -#define CLIENT_HEARD_BUCKETS 16 /* up to ~300 calls in heard list per client */ +#define CLIENT_HEARD_BUCKETS 32 /* up to ~300 calls in heard list per client */ struct client_heard_t { struct client_heard_t *next; struct client_heard_t **prevp; @@ -292,6 +295,7 @@ struct client_t { time_t connect_time;/* Time of connection */ time_t last_read; /* Time of last read - not necessarily last packet... */ time_t keepalive; /* Time of next keepalive chime */ + time_t cleanup; /* Time of next cleanup */ struct xpoll_fd_t *xfd; /* poll()/select() structure as defined in xpoll.h */