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:
parent
8dfef7e5e9
commit
809691aa3d
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
21
src/filter.c
21
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)) {
|
||||
|
|
|
|||
10
src/worker.h
10
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 */
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue