Skip to content

Instantly share code, notes, and snippets.

@hintjens
Created November 15, 2014 10:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hintjens/795ddedd577e3e0f00fc to your computer and use it in GitHub Desktop.
Save hintjens/795ddedd577e3e0f00fc to your computer and use it in GitHub Desktop.
test timers & tickets
/*
Shows relative performance of zloop timers vs tickets
The problem is when you have large numbers of DEALER clients talking
to a ROUTER server, and the server wants to expire idle clients with
some timeout, e.g. 30 seconds. Using zloop timers, this means deleting
and then recreating a timer for each received message. zloop does not
order its timers, and even if it did, finding a timer means searching
the list, an O(N) cost.
There are additional problems with the timer design of zloop, e.g.
zloop scans all timers each time it does a poll, which is potentially
for each recv operation. I may look at that later. For now my focus is
managing large sets of expiry timers.
There are various possible solutions to this problem, which get rather
complex. We can keep things simple by observing that a server typically
uses the same expiry period for all clients. Thus, our basic data
structure is an ordered list of clients or timers. The oldest client
or timer is at the list head, the youngest at the tail. Each time we
have a message from a client we take it out of the list and move it to
the tail.
To support this, zxlist works with node "handles" which let us move nodes
without searching the list.
zloop implements this as "tickets", which are expiry timers where a new
ticket always expires after the last ticket held so far, if any. The
existing zloop timers are then used for low-volume timers, and zloop
lets you enforce this with a zloop_set_timer_max () method.
This test case shows the relative difference in performance:
Testing zloop_timer insert/reset (1M simulated messages)
125 clients => 488 msecs
250 clients => 792 msecs
500 clients => 1505 msecs
1000 clients => 3690 msecs
2000 clients => 7853 msecs
Testing zloop_ticket insert/reset (1M simulated messages)
125 clients => 43 msecs
250 clients => 44 msecs
500 clients => 47 msecs
1000 clients => 50 msecs
2000 clients => 56 msecs
*/
#include <czmq.h>
static int
s_handle_timer (zloop_t *loop, int timer_id, void *arg)
{
return 0;
}
int main (void)
{
int nbr_messages = 1000000;
printf ("Testing zloop_timer insert/reset (1M simulated messages)\n");
int nbr_clients = 125;
while (nbr_clients < 4000) {
zloop_t *loop = zloop_new ();
int timer_id [nbr_clients];
int client_nbr;
for (client_nbr = 0; client_nbr < nbr_clients; client_nbr++)
timer_id [client_nbr] = zloop_timer (loop, 30000, 1, s_handle_timer, NULL);
int64_t start = zclock_time ();
int message_nbr;
for (message_nbr = 0; message_nbr < nbr_messages; message_nbr++) {
client_nbr = randof (nbr_clients);
zloop_timer_end (loop, timer_id [client_nbr]);
timer_id [client_nbr] = zloop_timer (loop, 30000, 1, s_handle_timer, NULL);
}
zloop_destroy (&loop);
printf ("%d clients => %d msecs\n\n", nbr_clients, (int) (zclock_time () - start));
nbr_clients *= 2;
}
nbr_clients = 125;
printf ("Testing zloop_ticket insert/reset (1M simulated messages)\n");
while (nbr_clients < 4000) {
zloop_t *loop = zloop_new ();
zloop_set_ticket_delay (loop, 30000);
void *handle [nbr_clients];
int client_nbr;
for (client_nbr = 0; client_nbr < nbr_clients; client_nbr++)
handle [client_nbr] = zloop_ticket (loop, s_handle_timer, NULL);
int64_t start = zclock_time ();
int message_nbr;
for (message_nbr = 0; message_nbr < nbr_messages; message_nbr++) {
client_nbr = randof (nbr_clients);
zloop_ticket_reset (loop, handle [client_nbr]);
}
printf ("%d clients => %d msecs\n", nbr_clients, (int) (zclock_time () - start));
zloop_destroy (&loop);
nbr_clients *= 2;
}
exit (0);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment