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.
This commit is contained in:
Heikki Hannikainen 2012-11-15 22:05:14 +02:00
parent 8dfef7e5e9
commit 809691aa3d
6 changed files with 120 additions and 56 deletions

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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;
}

View File

@ -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)) {

View File

@ -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 */