mirror of
http://git.haproxy.org/git/haproxy.git/
synced 2025-04-01 14:38:28 +00:00
Merge branch 'master' into timers
This commit is contained in:
commit
01ba1c909d
212
src/ev_sepoll.c
212
src/ev_sepoll.c
@ -105,6 +105,10 @@ static _syscall4 (int, epoll_wait, int, epfd, struct epoll_event *, events, int,
|
||||
|
||||
#define FD_EV_MASK (FD_EV_MASK_W | FD_EV_MASK_R)
|
||||
|
||||
/* This is the minimum number of events successfully processed in speculative
|
||||
* mode above which we agree to return without checking epoll() (1/2 times).
|
||||
*/
|
||||
#define MIN_RETURN_EVENTS 25
|
||||
|
||||
/* descriptor of one FD.
|
||||
* FIXME: should be a bit field */
|
||||
@ -215,6 +219,7 @@ REGPRM2 static int __fd_clr(const int fd, int dir)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* normally unused */
|
||||
REGPRM1 static void __fd_rem(int fd)
|
||||
{
|
||||
__fd_clr(fd, DIR_RD);
|
||||
@ -232,37 +237,13 @@ REGPRM1 static void __fd_clo(int fd)
|
||||
fd_list[fd].e &= ~(FD_EV_MASK);
|
||||
}
|
||||
|
||||
/*
|
||||
* operations to perform when reaching _do_poll() for some FDs in the spec
|
||||
* queue depending on their state. This is mainly used to cleanup some FDs
|
||||
* which are in STOP state. It is also used to compute EPOLL* flags when
|
||||
* switching from SPEC to WAIT.
|
||||
*/
|
||||
static struct ev_to_epoll {
|
||||
char op; // epoll opcode to switch from spec to wait, 0 if none
|
||||
char m; // inverted mask for existing events
|
||||
char ev; // remaining epoll events after change
|
||||
char pad;
|
||||
} ev_to_epoll[16] = {
|
||||
[FD_EV_IDLE_W | FD_EV_STOP_R] = { .op=EPOLL_CTL_DEL, .m=FD_EV_MASK_R },
|
||||
[FD_EV_SPEC_W | FD_EV_STOP_R] = { .op=EPOLL_CTL_DEL, .m=FD_EV_MASK_R },
|
||||
[FD_EV_STOP_W | FD_EV_IDLE_R] = { .op=EPOLL_CTL_DEL, .m=FD_EV_MASK_W },
|
||||
[FD_EV_STOP_W | FD_EV_SPEC_R] = { .op=EPOLL_CTL_DEL, .m=FD_EV_MASK_W },
|
||||
[FD_EV_WAIT_W | FD_EV_STOP_R] = { .op=EPOLL_CTL_MOD, .m=FD_EV_MASK_R, .ev=EPOLLOUT },
|
||||
[FD_EV_STOP_W | FD_EV_WAIT_R] = { .op=EPOLL_CTL_MOD, .m=FD_EV_MASK_W, .ev=EPOLLIN },
|
||||
[FD_EV_STOP_W | FD_EV_STOP_R] = { .op=EPOLL_CTL_DEL, .m=FD_EV_MASK_R|FD_EV_MASK_W },
|
||||
[FD_EV_SPEC_W | FD_EV_WAIT_R] = { .ev=EPOLLIN },
|
||||
[FD_EV_WAIT_W | FD_EV_SPEC_R] = { .ev=EPOLLOUT },
|
||||
[FD_EV_WAIT_W | FD_EV_WAIT_R] = { .ev=EPOLLIN|EPOLLOUT },
|
||||
};
|
||||
|
||||
/*
|
||||
* speculative epoll() poller
|
||||
*/
|
||||
REGPRM2 static void _do_poll(struct poller *p, struct timeval *exp)
|
||||
{
|
||||
static unsigned int last_skipped;
|
||||
int status;
|
||||
int status, eo;
|
||||
int fd, opcode;
|
||||
int count;
|
||||
int spec_idx;
|
||||
@ -270,105 +251,132 @@ REGPRM2 static void _do_poll(struct poller *p, struct timeval *exp)
|
||||
|
||||
|
||||
/* Here we have two options :
|
||||
* - either walk the list forwards and hope to atch more events
|
||||
* - either walk the list forwards and hope to match more events
|
||||
* - or walk it backwards to minimize the number of changes and
|
||||
* to make better use of the cache.
|
||||
* Tests have shown that walking backwards improves perf by 0.2%.
|
||||
*/
|
||||
|
||||
status = 0;
|
||||
spec_idx = nbspec;
|
||||
while (likely(spec_idx > 0)) {
|
||||
spec_idx--;
|
||||
fd = spec_list[spec_idx];
|
||||
eo = fd_list[fd].e; /* save old events */
|
||||
|
||||
/*
|
||||
* Process the speculative events.
|
||||
*
|
||||
* Principle: events which are marked FD_EV_SPEC are processed
|
||||
* with their assigned function. If the function returns 0, it
|
||||
* means there is nothing doable without polling first. We will
|
||||
* then convert the event to a pollable one by assigning them
|
||||
* the WAIT status.
|
||||
*/
|
||||
|
||||
fdtab[fd].ev = 0;
|
||||
if ((eo & FD_EV_MASK_R) == FD_EV_SPEC_R) {
|
||||
/* The owner is interested in reading from this FD */
|
||||
if (fdtab[fd].state != FD_STCLOSE && fdtab[fd].state != FD_STERROR) {
|
||||
/* Pretend there is something to read */
|
||||
fdtab[fd].ev |= FD_POLL_IN;
|
||||
if (!fdtab[fd].cb[DIR_RD].f(fd))
|
||||
fd_list[fd].e ^= (FD_EV_WAIT_R ^ FD_EV_SPEC_R);
|
||||
else
|
||||
status++;
|
||||
}
|
||||
}
|
||||
else if ((eo & FD_EV_MASK_R) == FD_EV_STOP_R) {
|
||||
/* This FD was being polled and is now being removed. */
|
||||
fd_list[fd].e &= ~FD_EV_MASK_R;
|
||||
}
|
||||
|
||||
if ((eo & FD_EV_MASK_W) == FD_EV_SPEC_W) {
|
||||
/* The owner is interested in writing to this FD */
|
||||
if (fdtab[fd].state != FD_STCLOSE && fdtab[fd].state != FD_STERROR) {
|
||||
/* Pretend there is something to write */
|
||||
fdtab[fd].ev |= FD_POLL_OUT;
|
||||
if (!fdtab[fd].cb[DIR_WR].f(fd))
|
||||
fd_list[fd].e ^= (FD_EV_WAIT_W ^ FD_EV_SPEC_W);
|
||||
else
|
||||
status++;
|
||||
}
|
||||
}
|
||||
else if ((eo & FD_EV_MASK_W) == FD_EV_STOP_W) {
|
||||
/* This FD was being polled and is now being removed. */
|
||||
fd_list[fd].e &= ~FD_EV_MASK_W;
|
||||
}
|
||||
|
||||
/* Now, we will adjust the event in the poll list. Indeed, it
|
||||
* is possible that an event which was previously in the poll
|
||||
* list now goes out, and the opposite is possible too. We can
|
||||
* have opposite changes for READ and WRITE too.
|
||||
*/
|
||||
|
||||
if ((eo ^ fd_list[fd].e) & FD_EV_RW_PL) {
|
||||
/* poll status changed*/
|
||||
if ((fd_list[fd].e & FD_EV_RW_PL) == 0) {
|
||||
/* fd removed from poll list */
|
||||
opcode = EPOLL_CTL_DEL;
|
||||
}
|
||||
else if ((eo & FD_EV_RW_PL) == 0) {
|
||||
/* new fd in the poll list */
|
||||
opcode = EPOLL_CTL_ADD;
|
||||
}
|
||||
else {
|
||||
/* fd status changed */
|
||||
opcode = EPOLL_CTL_MOD;
|
||||
}
|
||||
|
||||
/* construct the epoll events based on new state */
|
||||
ev.events = 0;
|
||||
if (fd_list[fd].e & FD_EV_WAIT_R)
|
||||
ev.events |= EPOLLIN;
|
||||
|
||||
if (fd_list[fd].e & FD_EV_WAIT_W)
|
||||
ev.events |= EPOLLOUT;
|
||||
|
||||
opcode = ev_to_epoll[fd_list[fd].e].op;
|
||||
if (opcode) {
|
||||
ev.data.fd = fd;
|
||||
ev.events = ev_to_epoll[fd_list[fd].e].ev;
|
||||
fd_list[fd].e &= ~(unsigned int)ev_to_epoll[fd_list[fd].e].m;
|
||||
epoll_ctl(epoll_fd, opcode, fd, &ev);
|
||||
}
|
||||
|
||||
|
||||
if (!(fd_list[fd].e & FD_EV_RW_SL)) {
|
||||
// This one must be removed. Let's clear it now.
|
||||
/* This fd switched to combinations of either WAIT or
|
||||
* IDLE. It must be removed from the spec list.
|
||||
*/
|
||||
delete_spec_entry(spec_idx);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* OK so now we do not have any event marked STOP anymore in
|
||||
* the list. We can simply try to execute functions for the
|
||||
* events we have found, and requeue them in case of EAGAIN.
|
||||
*/
|
||||
|
||||
status = 0;
|
||||
fdtab[fd].ev = 0;
|
||||
|
||||
if ((fd_list[fd].e & FD_EV_MASK_R) == FD_EV_SPEC_R) {
|
||||
if (fdtab[fd].state != FD_STCLOSE && fdtab[fd].state != FD_STERROR) {
|
||||
fdtab[fd].ev |= FD_POLL_IN;
|
||||
if (fdtab[fd].cb[DIR_RD].f(fd) == 0)
|
||||
status |= EPOLLIN;
|
||||
}
|
||||
}
|
||||
|
||||
if ((fd_list[fd].e & FD_EV_MASK_W) == FD_EV_SPEC_W) {
|
||||
if (fdtab[fd].state != FD_STCLOSE && fdtab[fd].state != FD_STERROR) {
|
||||
fdtab[fd].ev |= FD_POLL_OUT;
|
||||
if (fdtab[fd].cb[DIR_WR].f(fd) == 0)
|
||||
status |= EPOLLOUT;
|
||||
}
|
||||
}
|
||||
|
||||
if (status) {
|
||||
/* Some speculative accesses have failed, we must
|
||||
* switch to the WAIT state.
|
||||
*/
|
||||
ev.events = status;
|
||||
ev.data.fd = fd;
|
||||
if (fd_list[fd].e & FD_EV_RW_PL) {
|
||||
// Event already in poll list
|
||||
ev.events |= ev_to_epoll[fd_list[fd].e].ev;
|
||||
opcode = EPOLL_CTL_MOD;
|
||||
} else {
|
||||
// Event not in poll list yet
|
||||
opcode = EPOLL_CTL_ADD;
|
||||
}
|
||||
epoll_ctl(epoll_fd, opcode, fd, &ev);
|
||||
|
||||
if (status & EPOLLIN) {
|
||||
fd_list[fd].e &= ~FD_EV_MASK_R;
|
||||
fd_list[fd].e |= FD_EV_WAIT_R;
|
||||
}
|
||||
if (status & EPOLLOUT) {
|
||||
fd_list[fd].e &= ~FD_EV_MASK_W;
|
||||
fd_list[fd].e |= FD_EV_WAIT_W;
|
||||
}
|
||||
|
||||
if ((fd_list[fd].e & FD_EV_MASK_R) != FD_EV_SPEC_R &&
|
||||
(fd_list[fd].e & FD_EV_MASK_W) != FD_EV_SPEC_W) {
|
||||
delete_spec_entry(spec_idx);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If some speculative events remain, we must not set the timeout in
|
||||
* epoll_wait(). Also, if some speculative events remain, it means
|
||||
* that some have been immediately processed, otherwise they would
|
||||
* have been disabled.
|
||||
/* It may make sense to immediately return here if there are enough
|
||||
* processed events, without passing through epoll_wait() because we
|
||||
* have exactly done a poll.
|
||||
* Measures have shown a great performance increase if we call the
|
||||
* epoll_wait() only the second time after speculative accesses have
|
||||
* succeeded. This reduces the number of unsucessful calls to
|
||||
* epoll_wait() by a factor of about 3, and the total number of calls
|
||||
* by about 2.
|
||||
*/
|
||||
if (nbspec) {
|
||||
if (!last_skipped++) {
|
||||
/* Measures have shown a great performance increase if
|
||||
* we call the epoll_wait() only the second time after
|
||||
* speculative accesses have succeeded. This reduces
|
||||
* the number of unsucessful calls to epoll_wait() by
|
||||
* a factor of about 3, and the total number of calls
|
||||
* by about 2.
|
||||
*/
|
||||
|
||||
if (status >= MIN_RETURN_EVENTS) {
|
||||
/* We have processed at least MIN_RETURN_EVENTS, it's worth
|
||||
* returning now without checking epoll_wait().
|
||||
*/
|
||||
if (++last_skipped <= 1) {
|
||||
tv_now(&now);
|
||||
return;
|
||||
}
|
||||
}
|
||||
last_skipped = 0;
|
||||
|
||||
if (nbspec || status) {
|
||||
/* Maybe we have processed some events that we must report, or
|
||||
* maybe we still have events in the spec list, so we must not
|
||||
* wait in epoll() otherwise we will delay their delivery by
|
||||
* the next timeout.
|
||||
*/
|
||||
wait_time = 0;
|
||||
}
|
||||
else {
|
||||
@ -377,10 +385,10 @@ REGPRM2 static void _do_poll(struct poller *p, struct timeval *exp)
|
||||
else
|
||||
wait_time = -1;
|
||||
}
|
||||
last_skipped = 0;
|
||||
|
||||
/* now let's wait for events */
|
||||
/* now let's wait for real events */
|
||||
status = epoll_wait(epoll_fd, epoll_events, maxfd, wait_time);
|
||||
|
||||
tv_now(&now);
|
||||
|
||||
for (count = 0; count < status; count++) {
|
||||
|
218
tests/hash_results.txt
Normal file
218
tests/hash_results.txt
Normal file
@ -0,0 +1,218 @@
|
||||
Test: ./test_hashes | sort -k 3 -r
|
||||
|
||||
Note: haproxy_server_hash should be avoided as it's just a 32 bit XOR.
|
||||
|
||||
Athlon @ 1533 MHz, gcc-3.4 -march=i686 :
|
||||
haproxy_server_hash : 18477000 run/sec
|
||||
SuperFastHash : 6983511 run/sec
|
||||
hash_djbx33 : 4164334 run/sec
|
||||
bernstein : 3371838 run/sec
|
||||
kr_hash : 3257684 run/sec
|
||||
sax_hash : 3027567 run/sec
|
||||
fnv_hash : 2818374 run/sec
|
||||
haproxy_uri_hash : 2108346 run/sec
|
||||
oat_hash : 2106181 run/sec
|
||||
hashword : 1936973 run/sec
|
||||
hashpjw : 1803475 run/sec
|
||||
fnv_32a_str : 1499198 run/sec
|
||||
|
||||
Pentium-M @1700 MHz, gcc-3.4 -march=i686 :
|
||||
haproxy_server_hash : 15471737 run/sec
|
||||
SuperFastHash : 8155706 run/sec
|
||||
hash_djbx33 : 4520191 run/sec
|
||||
bernstein : 3956142 run/sec
|
||||
kr_hash : 3725125 run/sec
|
||||
fnv_hash : 3155413 run/sec
|
||||
sax_hash : 2688323 run/sec
|
||||
oat_hash : 2452789 run/sec
|
||||
haproxy_uri_hash : 2010853 run/sec
|
||||
hashword : 1831441 run/sec
|
||||
hashpjw : 1737000 run/sec
|
||||
fnv_32a_str : 1643737 run/sec
|
||||
|
||||
Athlon @ 1533 MHz, gcc-4.1 -march=i686 :
|
||||
haproxy_server_hash : 13592089 run/sec
|
||||
SuperFastHash2 : 8687957 run/sec
|
||||
SuperFastHash : 7361242 run/sec
|
||||
hash_djbx33 : 5741546 run/sec
|
||||
bernstein : 3368909 run/sec
|
||||
sax_hash : 3339880 run/sec
|
||||
kr_hash : 3277230 run/sec
|
||||
fnv_hash : 2832402 run/sec
|
||||
hashword : 2500317 run/sec
|
||||
haproxy_uri_hash : 2433241 run/sec
|
||||
oat_hash : 2403118 run/sec
|
||||
hashpjw : 1881229 run/sec
|
||||
fnv_32a_str : 1815709 run/sec
|
||||
|
||||
Pentium-M @1700 MHz, gcc-4.1 -march=i686 :
|
||||
haproxy_server_hash : 14128788 run/sec
|
||||
SuperFastHash2 : 8157119 run/sec
|
||||
SuperFastHash : 7481027 run/sec
|
||||
hash_djbx33 : 5660711 run/sec
|
||||
bernstein : 3961493 run/sec
|
||||
fnv_hash : 3590727 run/sec
|
||||
kr_hash : 3389393 run/sec
|
||||
sax_hash : 2667227 run/sec
|
||||
oat_hash : 2348211 run/sec
|
||||
hashword : 2278856 run/sec
|
||||
haproxy_uri_hash : 2098022 run/sec
|
||||
hashpjw : 1846583 run/sec
|
||||
fnv_32a_str : 1661219 run/sec
|
||||
|
||||
Pentium-M @600 MHz, gcc-4.1 -march=i686 :
|
||||
haproxy_server_hash : 5318468 run/sec
|
||||
SuperFastHash2 : 3126165 run/sec
|
||||
SuperFastHash : 2729981 run/sec
|
||||
hash_djbx33 : 2042181 run/sec
|
||||
bernstein : 1422927 run/sec
|
||||
fnv_hash : 1287736 run/sec
|
||||
kr_hash : 1217924 run/sec
|
||||
sax_hash : 949694 run/sec
|
||||
oat_hash : 837279 run/sec
|
||||
hashword : 812868 run/sec
|
||||
haproxy_uri_hash : 747611 run/sec
|
||||
hashpjw : 659890 run/sec
|
||||
fnv_32a_str : 590895 run/sec
|
||||
|
||||
athlon @ 1.5 GHz, gcc-2.95 -march=i686 :
|
||||
haproxy_server_hash : 13592864 run/sec
|
||||
SuperFastHash : 6931251 run/sec
|
||||
bernstein : 4105179 run/sec
|
||||
hash_djbx33 : 3920059 run/sec
|
||||
kr_hash : 2985794 run/sec
|
||||
fnv_hash : 2815457 run/sec
|
||||
sax_hash : 2791358 run/sec
|
||||
haproxy_uri_hash : 2786663 run/sec
|
||||
oat_hash : 2237859 run/sec
|
||||
hashword : 1985740 run/sec
|
||||
hashpjw : 1757733 run/sec
|
||||
fnv_32a_str : 1697299 run/sec
|
||||
|
||||
Pentium-M @ 600 MHz, gcc-2.95 -march=i686 :
|
||||
SuperFastHash : 2934387 run/sec
|
||||
haproxy_server_hash : 2864668 run/sec
|
||||
hash_djbx33 : 1498043 run/sec
|
||||
bernstein : 1414993 run/sec
|
||||
kr_hash : 1297907 run/sec
|
||||
fnv_hash : 1260343 run/sec
|
||||
sax_hash : 924764 run/sec
|
||||
oat_hash : 854545 run/sec
|
||||
haproxy_uri_hash : 790040 run/sec
|
||||
hashword : 693501 run/sec
|
||||
hashpjw : 647346 run/sec
|
||||
fnv_32a_str : 579691 run/sec
|
||||
|
||||
Pentium-M @ 1700 MHz, gcc-2.95 -march=i686 :
|
||||
SuperFastHash : 8006127 run/sec
|
||||
haproxy_server_hash : 7834162 run/sec
|
||||
hash_djbx33 : 4186025 run/sec
|
||||
bernstein : 3941492 run/sec
|
||||
kr_hash : 3630713 run/sec
|
||||
fnv_hash : 3507488 run/sec
|
||||
sax_hash : 2528128 run/sec
|
||||
oat_hash : 2395188 run/sec
|
||||
haproxy_uri_hash : 2158924 run/sec
|
||||
hashword : 1910992 run/sec
|
||||
hashpjw : 1819894 run/sec
|
||||
fnv_32a_str : 1629844 run/sec
|
||||
|
||||
UltraSparc @ 400 MHz, gcc-3.4.3 :
|
||||
haproxy_server_hash : 5573220 run/sec
|
||||
SuperFastHash : 1372714 run/sec
|
||||
bernstein : 1361733 run/sec
|
||||
hash_djbx33 : 1090373 run/sec
|
||||
sax_hash : 872499 run/sec
|
||||
oat_hash : 730354 run/sec
|
||||
kr_hash : 645431 run/sec
|
||||
haproxy_uri_hash : 541157 run/sec
|
||||
fnv_32a_str : 442608 run/sec
|
||||
hashpjw : 434858 run/sec
|
||||
fnv_hash : 401945 run/sec
|
||||
hashword : 340594 run/sec
|
||||
|
||||
UltraSparc @ 400 MHz, gcc-3.4.3 -mcpu=v9 :
|
||||
haproxy_server_hash : 5671183 run/sec
|
||||
bernstein : 1437122 run/sec
|
||||
hash_djbx33 : 1376294 run/sec
|
||||
SuperFastHash : 1306634 run/sec
|
||||
sax_hash : 873650 run/sec
|
||||
kr_hash : 801439 run/sec
|
||||
oat_hash : 729920 run/sec
|
||||
haproxy_uri_hash : 545341 run/sec
|
||||
hashpjw : 472190 run/sec
|
||||
fnv_32a_str : 443668 run/sec
|
||||
hashword : 357295 run/sec
|
||||
fnv_hash : 208823 run/sec
|
||||
|
||||
|
||||
Alpha EV6 @ 466 MHz, gcc-3.3 :
|
||||
haproxy_server_hash : 2495928 run/sec
|
||||
SuperFastHash : 2037208 run/sec
|
||||
hash_djbx33 : 1625092 run/sec
|
||||
kr_hash : 1532206 run/sec
|
||||
bernstein : 1256746 run/sec
|
||||
haproxy_uri_hash : 999106 run/sec
|
||||
oat_hash : 841943 run/sec
|
||||
sax_hash : 737447 run/sec
|
||||
hashpjw : 676170 run/sec
|
||||
fnv_hash : 644054 run/sec
|
||||
fnv_32a_str : 638526 run/sec
|
||||
hashword : 421777 run/sec
|
||||
|
||||
VIA EPIA @ 533 MHz, gcc-2.95 -march=i586 :
|
||||
haproxy_server_hash : 1391374 run/sec
|
||||
SuperFastHash : 912397 run/sec
|
||||
hash_djbx33 : 589868 run/sec
|
||||
kr_hash : 453706 run/sec
|
||||
bernstein : 437318 run/sec
|
||||
sax_hash : 334456 run/sec
|
||||
hashpjw : 316670 run/sec
|
||||
hashword : 315476 run/sec
|
||||
haproxy_uri_hash : 311112 run/sec
|
||||
oat_hash : 259127 run/sec
|
||||
fnv_32a_str : 229485 run/sec
|
||||
fnv_hash : 151620 run/sec
|
||||
|
||||
VIA EPIA @ 533 MHz, gcc-3.4 -march=i586 :
|
||||
haproxy_server_hash : 1660407 run/sec
|
||||
SuperFastHash : 791981 run/sec
|
||||
hash_djbx33 : 680498 run/sec
|
||||
kr_hash : 384076 run/sec
|
||||
bernstein : 377247 run/sec
|
||||
sax_hash : 355183 run/sec
|
||||
hashpjw : 298879 run/sec
|
||||
haproxy_uri_hash : 296748 run/sec
|
||||
oat_hash : 283932 run/sec
|
||||
hashword : 269429 run/sec
|
||||
fnv_32a_str : 204776 run/sec
|
||||
fnv_hash : 155301 run/sec
|
||||
|
||||
Pentium @ 133 MHz, gcc-3.4 -march=i586 :
|
||||
haproxy_server_hash : 930788 run/sec
|
||||
SuperFastHash : 344988 run/sec
|
||||
hash_djbx33 : 278996 run/sec
|
||||
bernstein : 211545 run/sec
|
||||
sax_hash : 185225 run/sec
|
||||
kr_hash : 156603 run/sec
|
||||
oat_hash : 135163 run/sec
|
||||
hashword : 128518 run/sec
|
||||
fnv_hash : 107024 run/sec
|
||||
haproxy_uri_hash : 105523 run/sec
|
||||
fnv_32a_str : 99913 run/sec
|
||||
hashpjw : 97860 run/sec
|
||||
|
||||
VAX VLC4000 @30 MHz, gcc-2.95 :
|
||||
haproxy_server_hash : 13208 run/sec
|
||||
hash_djbx33 : 12963 run/sec
|
||||
fnv_hash : 12150 run/sec
|
||||
SuperFastHash : 12037 run/sec
|
||||
bernstein : 11765 run/sec
|
||||
kr_hash : 11111 run/sec
|
||||
sax_hash : 7273 run/sec
|
||||
hashword : 7143 run/sec
|
||||
oat_hash : 6931 run/sec
|
||||
hashpjw : 6667 run/sec
|
||||
haproxy_uri_hash : 5714 run/sec
|
||||
fnv_32a_str : 4800 run/sec
|
||||
|
545
tests/test_hashes.c
Normal file
545
tests/test_hashes.c
Normal file
@ -0,0 +1,545 @@
|
||||
/*
|
||||
This file only show how many operations a hash is able to handle.
|
||||
It don't show the distribution nor collisions.
|
||||
|
||||
gcc -Wall -O3 -o test_hashes test_hashes.c
|
||||
./test_hashes |sort -k 3 -r
|
||||
*/
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
//#include <stdint.h>
|
||||
|
||||
|
||||
static struct timeval timeval_current(void)
|
||||
{
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
return tv;
|
||||
}
|
||||
|
||||
static double timeval_elapsed(struct timeval *tv)
|
||||
{
|
||||
struct timeval tv2 = timeval_current();
|
||||
return (tv2.tv_sec - tv->tv_sec) +
|
||||
(tv2.tv_usec - tv->tv_usec)*1.0e-6;
|
||||
}
|
||||
|
||||
#define HAPROXY_BACKENDS 4
|
||||
|
||||
unsigned long haproxy_uri_hash(char *uri, int uri_len){
|
||||
|
||||
unsigned long hash = 0;
|
||||
int c;
|
||||
|
||||
while (uri_len--) {
|
||||
c = *uri++;
|
||||
if (c == '?')
|
||||
break;
|
||||
hash = c + (hash << 6) + (hash << 16) - hash;
|
||||
}
|
||||
|
||||
return hash%HAPROXY_BACKENDS; /* I assume 4 active backends */
|
||||
} /* end haproxy_hash() */
|
||||
|
||||
/*
|
||||
* http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx
|
||||
*/
|
||||
unsigned sax_hash ( void *key, int len )
|
||||
{
|
||||
unsigned char *p = key;
|
||||
unsigned h = 0;
|
||||
int i;
|
||||
|
||||
for ( i = 0; i < len; i++ )
|
||||
h ^= ( h << 5 ) + ( h >> 2 ) + p[i];
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
#include <arpa/inet.h>
|
||||
/* len 4 for ipv4 and 16 for ipv6 */
|
||||
unsigned int haproxy_server_hash(const char *addr, int len){
|
||||
unsigned int h, l;
|
||||
l = h = 0;
|
||||
|
||||
while ((l + sizeof (int)) <= len) {
|
||||
h ^= ntohl(*(unsigned int *)(&addr[l]));
|
||||
l += sizeof (int);
|
||||
}
|
||||
return h %= HAPROXY_BACKENDS;
|
||||
}/* end haproxy_server_hash() */
|
||||
|
||||
|
||||
int hashpjw(const void *key) {
|
||||
|
||||
const char *ptr;
|
||||
unsigned int val;
|
||||
/*********************************************************************
|
||||
* *
|
||||
* Hash the key by performing a number of bit operations on it. *
|
||||
* *
|
||||
*********************************************************************/
|
||||
|
||||
val = 0;
|
||||
ptr = key;
|
||||
|
||||
while (*ptr != '\0') {
|
||||
|
||||
int tmp;
|
||||
|
||||
val = (val << 4) + (*ptr);
|
||||
|
||||
if((tmp = (val & 0xf0000000))) {
|
||||
val = val ^ (tmp >> 24);
|
||||
val = val ^ tmp;
|
||||
}
|
||||
ptr++;
|
||||
}/* end while */
|
||||
|
||||
return val;
|
||||
}/* end hashpjw */
|
||||
|
||||
static unsigned long
|
||||
hash_djbx33(
|
||||
register unsigned char *key,
|
||||
register size_t len)
|
||||
{
|
||||
register unsigned long hash = 5381;
|
||||
|
||||
/* the hash unrolled eight times */
|
||||
for (; len >= 8; len -= 8) {
|
||||
hash = ((hash << 5) + hash) + *key++;
|
||||
hash = ((hash << 5) + hash) + *key++;
|
||||
hash = ((hash << 5) + hash) + *key++;
|
||||
hash = ((hash << 5) + hash) + *key++;
|
||||
hash = ((hash << 5) + hash) + *key++;
|
||||
hash = ((hash << 5) + hash) + *key++;
|
||||
hash = ((hash << 5) + hash) + *key++;
|
||||
hash = ((hash << 5) + hash) + *key++;
|
||||
}
|
||||
switch (len) {
|
||||
case 7: hash = ((hash << 5) + hash) + *key++; /* fallthrough... */
|
||||
case 6: hash = ((hash << 5) + hash) + *key++; /* fallthrough... */
|
||||
case 5: hash = ((hash << 5) + hash) + *key++; /* fallthrough... */
|
||||
case 4: hash = ((hash << 5) + hash) + *key++; /* fallthrough... */
|
||||
case 3: hash = ((hash << 5) + hash) + *key++; /* fallthrough... */
|
||||
case 2: hash = ((hash << 5) + hash) + *key++; /* fallthrough... */
|
||||
case 1: hash = ((hash << 5) + hash) + *key++; break;
|
||||
default: /* case 0: */ break;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
typedef unsigned long int ub4; /* unsigned 4-byte quantities */
|
||||
typedef unsigned char ub1; /* unsigned 1-byte quantities */
|
||||
|
||||
ub4 bernstein(ub1 *key, ub4 len, ub4 level){
|
||||
ub4 hash = level;
|
||||
ub4 i;
|
||||
for (i=0; i<len; ++i) hash = 33*hash + key[i];
|
||||
return hash;
|
||||
}
|
||||
|
||||
/*
|
||||
* http://www.azillionmonkeys.com/qed/hash.html
|
||||
*/
|
||||
#undef get16bits
|
||||
#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \
|
||||
|| defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__)
|
||||
#define get16bits(d) (*((const uint16_t *) (d)))
|
||||
#endif
|
||||
|
||||
#if !defined (get16bits)
|
||||
#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8)\
|
||||
+(uint32_t)(((const uint8_t *)(d))[0]) )
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This function has a hole of 11 unused bits in bytes 2 and 3 of each block of
|
||||
* 32 bits.
|
||||
*/
|
||||
uint32_t SuperFastHash (const char * data, int len) {
|
||||
uint32_t hash = len, tmp;
|
||||
int rem;
|
||||
|
||||
if (len <= 0 || data == NULL) return 0;
|
||||
|
||||
rem = len & 3;
|
||||
len >>= 2;
|
||||
|
||||
/* Main loop */
|
||||
for (;len > 0; len--) {
|
||||
hash += get16bits (data);
|
||||
tmp = (get16bits (data+2) << 11) ^ hash;
|
||||
hash = (hash << 16) ^ tmp;
|
||||
data += 2*sizeof (uint16_t);
|
||||
hash += hash >> 11;
|
||||
}
|
||||
|
||||
/* Handle end cases */
|
||||
switch (rem) {
|
||||
case 3: hash += get16bits (data);
|
||||
hash ^= hash << 16;
|
||||
hash ^= data[sizeof (uint16_t)] << 18;
|
||||
hash += hash >> 11;
|
||||
break;
|
||||
case 2: hash += get16bits (data);
|
||||
hash ^= hash << 11;
|
||||
hash += hash >> 17;
|
||||
break;
|
||||
case 1: hash += *data;
|
||||
hash ^= hash << 10;
|
||||
hash += hash >> 1;
|
||||
}
|
||||
|
||||
/* Force "avalanching" of final 127 bits */
|
||||
hash ^= hash << 3;
|
||||
hash += hash >> 5;
|
||||
hash ^= hash << 4;
|
||||
hash += hash >> 17;
|
||||
hash ^= hash << 25;
|
||||
hash += hash >> 6;
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
/*
|
||||
* This variant uses all bits from the input block, and is about 15% faster.
|
||||
*/
|
||||
uint32_t SuperFastHash2 (const char * data, int len) {
|
||||
uint32_t hash = len, tmp;
|
||||
int rem;
|
||||
|
||||
if (len <= 0 || data == NULL) return 0;
|
||||
|
||||
rem = len & 3;
|
||||
len >>= 2;
|
||||
|
||||
/* Main loop */
|
||||
for (;len > 0; len--) {
|
||||
register uint32_t next;
|
||||
next = get16bits(data+2);
|
||||
hash += get16bits(data);
|
||||
tmp = ((next << 11) | (next >> 21)) ^ hash;
|
||||
hash = (hash << 16) ^ tmp;
|
||||
data += 2*sizeof (uint16_t);
|
||||
hash += hash >> 11;
|
||||
}
|
||||
|
||||
/* Handle end cases */
|
||||
switch (rem) {
|
||||
case 3: hash += get16bits (data);
|
||||
hash ^= hash << 16;
|
||||
hash ^= data[sizeof (uint16_t)] << 18;
|
||||
hash += hash >> 11;
|
||||
break;
|
||||
case 2: hash += get16bits (data);
|
||||
hash ^= hash << 11;
|
||||
hash += hash >> 17;
|
||||
break;
|
||||
case 1: hash += *data;
|
||||
hash ^= hash << 10;
|
||||
hash += hash >> 1;
|
||||
}
|
||||
|
||||
/* Force "avalanching" of final 127 bits */
|
||||
hash ^= hash << 3;
|
||||
hash += hash >> 5;
|
||||
hash ^= hash << 4;
|
||||
hash += hash >> 17;
|
||||
hash ^= hash << 25;
|
||||
hash += hash >> 6;
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
/*
|
||||
* 32 bit FNV-0 hash type
|
||||
*/
|
||||
typedef unsigned long Fnv32_t;
|
||||
|
||||
/*
|
||||
* fnv_32a_str - perform a 32 bit Fowler/Noll/Vo FNV-1a hash on a string
|
||||
*
|
||||
* input:
|
||||
* str - string to hash
|
||||
* hval - previous hash value or 0 if first call
|
||||
*
|
||||
* returns:
|
||||
* 32 bit hash as a static hash type
|
||||
*
|
||||
* NOTE: To use the recommended 32 bit FNV-1a hash, use FNV1_32A_INIT as the
|
||||
* hval arg on the first call to either fnv_32a_buf() or fnv_32a_str().
|
||||
*/
|
||||
Fnv32_t
|
||||
fnv_32a_str(char *str, Fnv32_t hval)
|
||||
{
|
||||
unsigned char *s = (unsigned char *)str; /* unsigned string */
|
||||
|
||||
/*
|
||||
* FNV-1a hash each octet in the buffer
|
||||
*/
|
||||
while (*s) {
|
||||
|
||||
/* xor the bottom with the current octet */
|
||||
hval ^= (Fnv32_t)*s++;
|
||||
|
||||
/* #define NO_FNV_GCC_OPTIMIZATION */
|
||||
/* multiply by the 32 bit FNV magic prime mod 2^32 */
|
||||
#if defined(NO_FNV_GCC_OPTIMIZATION)
|
||||
/*
|
||||
* 32 bit magic FNV-1a prime
|
||||
*/
|
||||
#define FNV_32_PRIME ((Fnv32_t)0x01000193)
|
||||
hval *= FNV_32_PRIME;
|
||||
#else
|
||||
hval += (hval<<1) + (hval<<4) + (hval<<7) + (hval<<8) + (hval<<24);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* return our new hash value */
|
||||
return hval;
|
||||
}
|
||||
|
||||
/*
|
||||
* from lookup3.c, by Bob Jenkins, May 2006, Public Domain.
|
||||
*/
|
||||
|
||||
#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k))))
|
||||
|
||||
/*
|
||||
-------------------------------------------------------------------------------
|
||||
mix -- mix 3 32-bit values reversibly.
|
||||
|
||||
This is reversible, so any information in (a,b,c) before mix() is
|
||||
still in (a,b,c) after mix().
|
||||
|
||||
If four pairs of (a,b,c) inputs are run through mix(), or through
|
||||
mix() in reverse, there are at least 32 bits of the output that
|
||||
are sometimes the same for one pair and different for another pair.
|
||||
This was tested for:
|
||||
* pairs that differed by one bit, by two bits, in any combination
|
||||
of top bits of (a,b,c), or in any combination of bottom bits of
|
||||
(a,b,c).
|
||||
* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed
|
||||
the output delta to a Gray code (a^(a>>1)) so a string of 1's (as
|
||||
is commonly produced by subtraction) look like a single 1-bit
|
||||
difference.
|
||||
* the base values were pseudorandom, all zero but one bit set, or
|
||||
all zero plus a counter that starts at zero.
|
||||
|
||||
Some k values for my "a-=c; a^=rot(c,k); c+=b;" arrangement that
|
||||
satisfy this are
|
||||
4 6 8 16 19 4
|
||||
9 15 3 18 27 15
|
||||
14 9 3 7 17 3
|
||||
Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing
|
||||
for "differ" defined as + with a one-bit base and a two-bit delta. I
|
||||
used http://burtleburtle.net/bob/hash/avalanche.html to choose
|
||||
the operations, constants, and arrangements of the variables.
|
||||
|
||||
This does not achieve avalanche. There are input bits of (a,b,c)
|
||||
that fail to affect some output bits of (a,b,c), especially of a. The
|
||||
most thoroughly mixed value is c, but it doesn't really even achieve
|
||||
avalanche in c.
|
||||
|
||||
This allows some parallelism. Read-after-writes are good at doubling
|
||||
the number of bits affected, so the goal of mixing pulls in the opposite
|
||||
direction as the goal of parallelism. I did what I could. Rotates
|
||||
seem to cost as much as shifts on every machine I could lay my hands
|
||||
on, and rotates are much kinder to the top and bottom bits, so I used
|
||||
rotates.
|
||||
-------------------------------------------------------------------------------
|
||||
*/
|
||||
#define mix(a,b,c) \
|
||||
{ \
|
||||
a -= c; a ^= rot(c, 4); c += b; \
|
||||
b -= a; b ^= rot(a, 6); a += c; \
|
||||
c -= b; c ^= rot(b, 8); b += a; \
|
||||
a -= c; a ^= rot(c,16); c += b; \
|
||||
b -= a; b ^= rot(a,19); a += c; \
|
||||
c -= b; c ^= rot(b, 4); b += a; \
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------------------------------------------------------------
|
||||
final -- final mixing of 3 32-bit values (a,b,c) into c
|
||||
|
||||
Pairs of (a,b,c) values differing in only a few bits will usually
|
||||
produce values of c that look totally different. This was tested for
|
||||
* pairs that differed by one bit, by two bits, in any combination
|
||||
of top bits of (a,b,c), or in any combination of bottom bits of
|
||||
(a,b,c).
|
||||
* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed
|
||||
the output delta to a Gray code (a^(a>>1)) so a string of 1's (as
|
||||
is commonly produced by subtraction) look like a single 1-bit
|
||||
difference.
|
||||
* the base values were pseudorandom, all zero but one bit set, or
|
||||
all zero plus a counter that starts at zero.
|
||||
|
||||
These constants passed:
|
||||
14 11 25 16 4 14 24
|
||||
12 14 25 16 4 14 24
|
||||
and these came close:
|
||||
4 8 15 26 3 22 24
|
||||
10 8 15 26 3 22 24
|
||||
11 8 15 26 3 22 24
|
||||
-------------------------------------------------------------------------------
|
||||
*/
|
||||
#define final(a,b,c) \
|
||||
{ \
|
||||
c ^= b; c -= rot(b,14); \
|
||||
a ^= c; a -= rot(c,11); \
|
||||
b ^= a; b -= rot(a,25); \
|
||||
c ^= b; c -= rot(b,16); \
|
||||
a ^= c; a -= rot(c,4); \
|
||||
b ^= a; b -= rot(a,14); \
|
||||
c ^= b; c -= rot(b,24); \
|
||||
}
|
||||
|
||||
/*
|
||||
--------------------------------------------------------------------
|
||||
This works on all machines. To be useful, it requires
|
||||
-- that the key be an array of uint32_t's, and
|
||||
-- that the length be the number of uint32_t's in the key
|
||||
|
||||
The function hashword() is identical to hashlittle() on little-endian
|
||||
machines, and identical to hashbig() on big-endian machines,
|
||||
except that the length has to be measured in uint32_ts rather than in
|
||||
bytes. hashlittle() is more complicated than hashword() only because
|
||||
hashlittle() has to dance around fitting the key bytes into registers.
|
||||
--------------------------------------------------------------------
|
||||
*/
|
||||
uint32_t hashword(
|
||||
const uint32_t *k, /* the key, an array of uint32_t values */
|
||||
size_t length, /* the length of the key, in uint32_ts */
|
||||
uint32_t initval) /* the previous hash, or an arbitrary value */
|
||||
{
|
||||
uint32_t a,b,c;
|
||||
|
||||
/* Set up the internal state */
|
||||
a = b = c = 0xdeadbeef + (((uint32_t)length)<<2) + initval;
|
||||
|
||||
/*------------------------------------------------- handle most of the key */
|
||||
while (length > 3)
|
||||
{
|
||||
a += k[0];
|
||||
b += k[1];
|
||||
c += k[2];
|
||||
mix(a,b,c);
|
||||
length -= 3;
|
||||
k += 3;
|
||||
}
|
||||
|
||||
/*------------------------------------------- handle the last 3 uint32_t's */
|
||||
switch(length) /* all the case statements fall through */
|
||||
{
|
||||
case 3 : c+=k[2];
|
||||
case 2 : b+=k[1];
|
||||
case 1 : a+=k[0];
|
||||
final(a,b,c);
|
||||
case 0: /* case 0: nothing left to add */
|
||||
break;
|
||||
}
|
||||
/*------------------------------------------------------ report the result */
|
||||
return c;
|
||||
}
|
||||
|
||||
/* from K&R book site 139 */
|
||||
#define HASHSIZE 101
|
||||
|
||||
unsigned kr_hash(char *s){
|
||||
unsigned hashval;
|
||||
|
||||
for(hashval = 0; *s != '\0';s++)
|
||||
hashval = *s + 31 * hashval;
|
||||
|
||||
return hashval % HASHSIZE;
|
||||
|
||||
} /* end kr_hash() */
|
||||
|
||||
unsigned fnv_hash ( void *key, int len )
|
||||
{
|
||||
unsigned char *p = key;
|
||||
unsigned h = 2166136261;
|
||||
int i;
|
||||
|
||||
for ( i = 0; i < len; i++ )
|
||||
h = ( h * 16777619 ) ^ p[i];
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
unsigned oat_hash ( void *key, int len )
|
||||
{
|
||||
unsigned char *p = key;
|
||||
unsigned h = 0;
|
||||
int i;
|
||||
|
||||
for ( i = 0; i < len; i++ ) {
|
||||
h += p[i];
|
||||
h += ( h << 10 );
|
||||
h ^= ( h >> 6 );
|
||||
}
|
||||
|
||||
h += ( h << 3 );
|
||||
h ^= ( h >> 11 );
|
||||
h += ( h << 15 );
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
|
||||
#define run_test(fct, args) { \
|
||||
unsigned long loop, count; \
|
||||
volatile unsigned long result; \
|
||||
double delta; \
|
||||
struct timeval tv; \
|
||||
fprintf(stderr, "Starting %s\n", #fct); \
|
||||
tv = timeval_current(); \
|
||||
count = 0; \
|
||||
do { \
|
||||
delta = timeval_elapsed(&tv); \
|
||||
for (loop = 0; loop < 1000; loop++) { \
|
||||
result = fct args; \
|
||||
count++; \
|
||||
} \
|
||||
} while (delta < 1.0); \
|
||||
fprintf(stdout, "%-20s : %10.0f run/sec\n", #fct, count/delta); \
|
||||
fflush(stdout); \
|
||||
}
|
||||
|
||||
int main(){
|
||||
|
||||
char **start;
|
||||
int len;
|
||||
|
||||
char *urls[] = {
|
||||
"http://www.microsoft.com/shared/core/1/webservice/navigation.asmx/DisplayDownlevelNavHtml",
|
||||
NULL
|
||||
};
|
||||
|
||||
start = urls;
|
||||
len = strlen(*urls);
|
||||
|
||||
run_test(SuperFastHash2, (*urls, len));
|
||||
run_test(SuperFastHash, (*urls, len));
|
||||
run_test(haproxy_uri_hash, (*urls, len));
|
||||
run_test(haproxy_server_hash, (*urls, len));
|
||||
run_test(hashpjw, (*urls));
|
||||
run_test(hash_djbx33, ((unsigned char *)*urls, len));
|
||||
run_test(bernstein, ((unsigned char *)*urls, len, 4));
|
||||
run_test(fnv_32a_str, (*urls, 0));
|
||||
run_test(hashword, ((const uint32_t *)*urls,strlen(*urls),0));
|
||||
run_test(kr_hash, (*urls));
|
||||
run_test(sax_hash, (*urls, len));
|
||||
run_test(fnv_hash, (*urls, len));
|
||||
run_test(oat_hash, (*urls, len));
|
||||
|
||||
return 0;
|
||||
|
||||
}/* end main() */
|
165
tests/uri_hash.c
165
tests/uri_hash.c
@ -1,4 +1,6 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#define NSERV 10
|
||||
#define MAXLINE 1000
|
||||
@ -11,7 +13,7 @@ static unsigned long hash_gd1(char *uri)
|
||||
unsigned long hash = 0;
|
||||
int c;
|
||||
|
||||
while (c = *uri++)
|
||||
while ((c = *uri++))
|
||||
hash = c + (hash << 6) + (hash << 16) - hash;
|
||||
|
||||
return hash;
|
||||
@ -23,7 +25,7 @@ static unsigned long hash_gd2(char *uri)
|
||||
unsigned long hash = 0;
|
||||
int c;
|
||||
|
||||
while (c = *uri++) {
|
||||
while ((c = *uri++)) {
|
||||
if (c == '?' || c == '\n')
|
||||
break;
|
||||
hash = c + (hash << 6) + (hash << 16) - hash;
|
||||
@ -39,7 +41,7 @@ static unsigned long hash_gd3(char *uri)
|
||||
unsigned long hash = 0;
|
||||
int c;
|
||||
|
||||
while (c = *uri++) {
|
||||
while ((c = *uri++)) {
|
||||
if (c == '?' || c == '\n')
|
||||
break;
|
||||
hash = c - (hash << 3) + (hash << 15) - hash;
|
||||
@ -55,7 +57,7 @@ static unsigned long hash_gd4(char *uri)
|
||||
unsigned long hash = 0;
|
||||
int c;
|
||||
|
||||
while (c = *uri++) {
|
||||
while ((c = *uri++)) {
|
||||
if (c == '?' || c == '\n')
|
||||
break;
|
||||
hash = hash + (hash << 6) - (hash << 15) - c;
|
||||
@ -71,7 +73,7 @@ static unsigned long hash_gd5(char *uri)
|
||||
unsigned long hash = 0;
|
||||
int c;
|
||||
|
||||
while (c = *uri++) {
|
||||
while ((c = *uri++)) {
|
||||
if (c == '?' || c == '\n')
|
||||
break;
|
||||
hash = hash + (hash << 2) - (hash << 19) - c;
|
||||
@ -87,7 +89,7 @@ static unsigned long hash_gd6(char *uri)
|
||||
unsigned long hash = 0;
|
||||
int c;
|
||||
|
||||
while (c = *uri++) {
|
||||
while ((c = *uri++)) {
|
||||
if (c == '?' || c == '\n')
|
||||
break;
|
||||
hash = hash + (hash << 2) - (hash << 22) - c;
|
||||
@ -152,7 +154,7 @@ static unsigned int rev32(unsigned int c) {
|
||||
|
||||
int hash_wt2(const char *src, int len) {
|
||||
unsigned int i = 0x3C964BA5; /* as many ones as zeroes */
|
||||
unsigned int j, k;
|
||||
unsigned int j;
|
||||
unsigned int ih, il;
|
||||
int bit;
|
||||
|
||||
@ -171,6 +173,142 @@ int hash_wt2(const char *src, int len) {
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
//typedef unsigned int uint32_t;
|
||||
//typedef unsigned short uint8_t;
|
||||
//typedef unsigned char uint16_t;
|
||||
|
||||
/*
|
||||
* http://www.azillionmonkeys.com/qed/hash.html
|
||||
*/
|
||||
#undef get16bits
|
||||
#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \
|
||||
|| defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__)
|
||||
#define get16bits(d) (*((const uint16_t *) (d)))
|
||||
#endif
|
||||
|
||||
#if !defined (get16bits)
|
||||
#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8)\
|
||||
+(uint32_t)(((const uint8_t *)(d))[0]) )
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This function has a hole of 11 unused bits in bytes 2 and 3 of each block of
|
||||
* 32 bits.
|
||||
*/
|
||||
int counts_SuperFastHash[NSERV][NSERV];
|
||||
|
||||
uint32_t SuperFastHash (const char * data, int len) {
|
||||
uint32_t hash = len, tmp;
|
||||
int rem;
|
||||
|
||||
if (len <= 0 || data == NULL) return 0;
|
||||
|
||||
rem = len & 3;
|
||||
len >>= 2;
|
||||
|
||||
/* Main loop */
|
||||
for (;len > 0; len--) {
|
||||
hash += get16bits (data);
|
||||
tmp = (get16bits (data+2) << 11) ^ hash;
|
||||
hash = (hash << 16) ^ tmp;
|
||||
data += 2*sizeof (uint16_t);
|
||||
hash += hash >> 11;
|
||||
}
|
||||
|
||||
/* Handle end cases */
|
||||
switch (rem) {
|
||||
case 3: hash += get16bits (data);
|
||||
hash ^= hash << 16;
|
||||
hash ^= data[sizeof (uint16_t)] << 18;
|
||||
hash += hash >> 11;
|
||||
break;
|
||||
case 2: hash += get16bits (data);
|
||||
hash ^= hash << 11;
|
||||
hash += hash >> 17;
|
||||
break;
|
||||
case 1: hash += *data;
|
||||
hash ^= hash << 10;
|
||||
hash += hash >> 1;
|
||||
}
|
||||
|
||||
/* Force "avalanching" of final 127 bits */
|
||||
hash ^= hash << 3;
|
||||
hash += hash >> 5;
|
||||
hash ^= hash << 4;
|
||||
hash += hash >> 17;
|
||||
hash ^= hash << 25;
|
||||
hash += hash >> 6;
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
/*
|
||||
* This variant uses all bits from the input block, and is about 15% faster.
|
||||
*/
|
||||
int counts_SuperFastHash2[NSERV][NSERV];
|
||||
uint32_t SuperFastHash2 (const char * data, int len) {
|
||||
uint32_t hash = len, tmp;
|
||||
int rem;
|
||||
|
||||
if (len <= 0 || data == NULL) return 0;
|
||||
|
||||
rem = len & 3;
|
||||
len >>= 2;
|
||||
|
||||
/* Main loop */
|
||||
for (;len > 0; len--) {
|
||||
register uint32_t next;
|
||||
next = get16bits(data+2);
|
||||
hash += get16bits(data);
|
||||
tmp = ((next << 11) | (next >> 21)) ^ hash;
|
||||
hash = (hash << 16) ^ tmp;
|
||||
data += 2*sizeof (uint16_t);
|
||||
hash += hash >> 11;
|
||||
}
|
||||
|
||||
/* Handle end cases */
|
||||
switch (rem) {
|
||||
case 3: hash += get16bits (data);
|
||||
hash ^= hash << 16;
|
||||
hash ^= data[sizeof (uint16_t)] << 18;
|
||||
hash += hash >> 11;
|
||||
break;
|
||||
case 2: hash += get16bits (data);
|
||||
hash ^= hash << 11;
|
||||
hash += hash >> 17;
|
||||
break;
|
||||
case 1: hash += *data;
|
||||
hash ^= hash << 10;
|
||||
hash += hash >> 1;
|
||||
}
|
||||
|
||||
/* Force "avalanching" of final 127 bits */
|
||||
hash ^= hash << 3;
|
||||
hash += hash >> 5;
|
||||
hash ^= hash << 4;
|
||||
hash += hash >> 17;
|
||||
hash ^= hash << 25;
|
||||
hash += hash >> 6;
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
/* len 4 for ipv4 and 16 for ipv6 */
|
||||
int counts_srv[NSERV][NSERV];
|
||||
unsigned int haproxy_server_hash(const char *addr, int len){
|
||||
unsigned int h, l;
|
||||
l = h = 0;
|
||||
|
||||
while ((l + sizeof (int)) <= len) {
|
||||
h ^= ntohl(*(unsigned int *)(&addr[l]));
|
||||
l += sizeof (int);
|
||||
}
|
||||
return h;
|
||||
}/* end haproxy_server_hash() */
|
||||
|
||||
|
||||
|
||||
void count_hash_results(unsigned long hash, int counts[NSERV][NSERV]) {
|
||||
int srv, nsrv;
|
||||
|
||||
@ -196,7 +334,7 @@ void dump_hash_results(char *name, int counts[NSERV][NSERV]) {
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
main() {
|
||||
int main() {
|
||||
memset(counts_gd1, 0, sizeof(counts_gd1));
|
||||
memset(counts_gd2, 0, sizeof(counts_gd2));
|
||||
memset(counts_gd3, 0, sizeof(counts_gd3));
|
||||
@ -205,6 +343,9 @@ main() {
|
||||
memset(counts_gd6, 0, sizeof(counts_gd6));
|
||||
memset(counts_wt1, 0, sizeof(counts_wt1));
|
||||
memset(counts_wt2, 0, sizeof(counts_wt2));
|
||||
memset(counts_srv, 0, sizeof(counts_srv));
|
||||
memset(counts_SuperFastHash, 0, sizeof(counts_SuperFastHash));
|
||||
memset(counts_SuperFastHash2, 0, sizeof(counts_SuperFastHash2));
|
||||
|
||||
while (fgets(line, MAXLINE, stdin) != NULL) {
|
||||
count_hash_results(hash_gd1(line), counts_gd1);
|
||||
@ -215,6 +356,9 @@ main() {
|
||||
count_hash_results(hash_gd6(line), counts_gd6);
|
||||
count_hash_results(hash_wt1(31, line), counts_wt1);
|
||||
count_hash_results(hash_wt2(line, strlen(line)), counts_wt2);
|
||||
count_hash_results(haproxy_server_hash(line, strlen(line)), counts_srv);
|
||||
count_hash_results(SuperFastHash(line, strlen(line)), counts_SuperFastHash);
|
||||
count_hash_results(SuperFastHash2(line, strlen(line)), counts_SuperFastHash2);
|
||||
}
|
||||
|
||||
dump_hash_results("hash_gd1", counts_gd1);
|
||||
@ -225,4 +369,9 @@ main() {
|
||||
dump_hash_results("hash_gd6", counts_gd6);
|
||||
dump_hash_results("hash_wt1", counts_wt1);
|
||||
dump_hash_results("hash_wt2", counts_wt2);
|
||||
dump_hash_results("haproxy_server_hash", counts_srv);
|
||||
dump_hash_results("SuperFastHash", counts_SuperFastHash);
|
||||
dump_hash_results("SuperFastHash2", counts_SuperFastHash2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user