1
0
mirror of https://github.com/samba-team/samba.git synced 2025-01-14 19:24:43 +03:00
Rusty Russell 1beb793664 lib/ccan/htable, strset: benchmarking tools.
This lets us compare hash table vs. strset vs. the example
implementation of critbit trees.

cbspeed 100 runs, min-max(avg):
#01: Initial insert:   236-245(237)
#02: Initial lookup (match):   180-186(180)
#03: Initial lookup (miss):   171-185(172)
#04: Initial lookup (random):   441-457(444)
#05: Initial delete all:   127-132(128)
#06: Initial re-inserting:   219-225(220)
#07: Deleting first half:   101-104(102)
#08: Adding (a different) half:   158-162(159)
#09: Lookup after half-change (match):   202-207(203)
#10: Lookup after half-change (miss):   217-222(218)
#11: Churn 1:   297-302(299)
#12: Churn 2:   297-305(300)
#13: Churn 3:   301-308(303)
#14: Post-Churn lookup (match):   189-195(190)
#15: Post-Churn lookup (miss):   189-193(190)
#16: Post-Churn lookup (random):   499-513(503)

speed 100 runs, min-max(avg):
#01: Initial insert:   211-218(212)
#02: Initial lookup (match):   161-166(162)
#03: Initial lookup (miss):   157-162(158)
#04: Initial lookup (random):   452-460(454)
#05: Initial delete all:   126-135(127)
#06: Initial re-inserting:   193-201(194)
#07: Deleting first half:   99-107(99)
#08: Adding (a different) half:   143-190(144)
#09: Lookup after half-change (match):   183-195(184)
#10: Lookup after half-change (miss):   197-203(198)
#11: Churn 1:   271-278(274)
#12: Churn 2:   280-287(282)
#13: Churn 3:   277-285(279)
#14: Post-Churn lookup (match):   171-175(171)
#15: Post-Churn lookup (miss):   174-178(175)
#16: Post-Churn lookup (random):   525-552(528)

stringspeed 100 runs, min-max(avg):
#01: Initial insert:   300-343(308)
#02: Initial lookup (match):   98-136(99)
#03: Initial lookup (miss):   73-102(75)
#04: Initial lookup (random):   230-282(233)
#05: Initial delete all:   66-102(69)
#06: Initial re-inserting:   62-99(64)
#07: Deleting first half:   43-52(43)
#08: Adding (a different) half:   101-156(106)
#09: Lookup after half-change (match):   114-156(120)
#10: Lookup after half-change (miss):   94-103(95)
#11: Churn 1:   98-105(99)
#12: Churn 2:   96-104(98)
#13: Churn 3:   174-184(176)
#14: Post-Churn lookup (match):   93-112(94)
#15: Post-Churn lookup (miss):   77-107(79)
#16: Post-Churn lookup (random):   229-265(232)

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
(Imported from CCAN commit 5c559e7df1d31b4c0ddf26451fac972dc8a0c2c9)
2011-12-05 16:42:46 +10:30

591 lines
11 KiB
C

/* Simple speed tests using original critbit code (modified not to allocate).
*
* Results on my 32 bit Intel(R) Core(TM) i5 CPU M 560 @ 2.67GHz, gcc 4.5.2:
* Run 100 times: Min-Max(Avg)
#01: Initial insert: 237-257(239)
#02: Initial lookup (match): 180-197(181)
#03: Initial lookup (miss): 171-190(172)
#04: Initial lookup (random): 441-455(446)
#05: Initial delete all: 127-148(128)
#06: Initial re-inserting: 219-298(221)
#07: Deleting first half: 101-109(102)
#08: Adding (a different) half: 159-165(160)
#09: Lookup after half-change (match): 203-216(204)
#10: Lookup after half-change (miss): 217-225(218)
#11: Churn 1: 298-311(300)
#12: Churn 2: 298-318(301)
#13: Churn 3: 301-322(304)
#14: Post-Churn lookup (match): 189-196(190)
#15: Post-Churn lookup (miss): 189-197(191)
#16: Post-Churn lookup (random): 500-531(506)
*/
#include <ccan/str_talloc/str_talloc.h>
#include <ccan/grab_file/grab_file.h>
#include <ccan/talloc/talloc.h>
#include <ccan/time/time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <sys/time.h>
/* CRITBIT source */
typedef struct {
void *root;
} critbit0_tree;
int critbit0_contains(critbit0_tree *t, const char *u);
int critbit0_insert(critbit0_tree *t, const char *u);
int critbit0_delete(critbit0_tree *t, const char *u);
void critbit0_clear(critbit0_tree *t);
int critbit0_allprefixed(critbit0_tree *t, const char *prefix,
int (*handle) (const char *, void *), void *arg);
#define uint8 uint8_t
#define uint32 uint32_t
static size_t allocated;
/*2:*/
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <errno.h>
typedef struct{
void*child[2];
uint32 byte;
uint8 otherbits;
}critbit0_node;
/*:2*//*3:*/
int
critbit0_contains(critbit0_tree*t,const char*u){
const uint8*ubytes= (void*)u;
const size_t ulen= strlen(u);
uint8*p= t->root;
/*4:*/
if(!p)return 0;
/*:4*/
/*5:*/
while(1&(intptr_t)p){
critbit0_node*q= (void*)(p-1);
/*6:*/
uint8 c= 0;
if(q->byte<ulen)c= ubytes[q->byte];
const int direction= (1+(q->otherbits|c))>>8;
/*:6*/
p= q->child[direction];
}
/*:5*/
/*7:*/
return 0==strcmp(u,(const char*)p);
/*:7*/
}
/*:3*//*8:*/
int critbit0_insert(critbit0_tree*t,const char*u)
{
const uint8*const ubytes= (void*)u;
const size_t ulen= strlen(u);
uint8*p= t->root;
/*9:*/
if(!p){
#if 0
char*x;
int a= posix_memalign((void**)&x,sizeof(void*),ulen+1);
if(a)return 0;
memcpy(x,u,ulen+1);
t->root= x;
#else
t->root = (char *)u;
#endif
return 2;
}
/*:9*/
/*5:*/
while(1&(intptr_t)p){
critbit0_node*q= (void*)(p-1);
/*6:*/
uint8 c= 0;
if(q->byte<ulen)c= ubytes[q->byte];
const int direction= (1+(q->otherbits|c))>>8;
/*:6*/
p= q->child[direction];
}
/*:5*/
/*10:*/
/*11:*/
uint32 newbyte;
uint32 newotherbits;
for(newbyte= 0;newbyte<ulen;++newbyte){
if(p[newbyte]!=ubytes[newbyte]){
newotherbits= p[newbyte]^ubytes[newbyte];
goto different_byte_found;
}
}
if(p[newbyte]!=0){
newotherbits= p[newbyte];
goto different_byte_found;
}
return 1;
different_byte_found:
/*:11*/
/*12:*/
while(newotherbits&(newotherbits-1))newotherbits&= newotherbits-1;
newotherbits^= 255;
uint8 c= p[newbyte];
int newdirection= (1+(newotherbits|c))>>8;
/*:12*/
/*:10*/
/*13:*/
/*14:*/
critbit0_node*newnode;
if(posix_memalign((void**)&newnode,sizeof(void*),sizeof(critbit0_node)))return 0;
allocated++;
char*x;
#if 0
if(posix_memalign((void**)&x,sizeof(void*),ulen+1)){
free(newnode);
return 0;
}
memcpy(x,ubytes,ulen+1);
#else
x = (char *)u;
#endif
newnode->byte= newbyte;
newnode->otherbits= newotherbits;
newnode->child[1-newdirection]= x;
/*:14*/
/*15:*/
void**wherep= &t->root;
for(;;){
uint8*p= *wherep;
if(!(1&(intptr_t)p))break;
critbit0_node*q= (void*)(p-1);
if(q->byte> newbyte)break;
if(q->byte==newbyte&&q->otherbits> newotherbits)break;
uint8 c= 0;
if(q->byte<ulen)c= ubytes[q->byte];
const int direction= (1+(q->otherbits|c))>>8;
wherep= q->child+direction;
}
newnode->child[newdirection]= *wherep;
*wherep= (void*)(1+(char*)newnode);
/*:15*/
/*:13*/
return 2;
}
/*:8*//*16:*/
int critbit0_delete(critbit0_tree*t,const char*u){
const uint8*ubytes= (void*)u;
const size_t ulen= strlen(u);
uint8*p= t->root;
void**wherep= &t->root;
void**whereq= 0;
critbit0_node*q= 0;
int direction= 0;
/*17:*/
if(!p)return 0;
/*:17*/
/*18:*/
while(1&(intptr_t)p){
whereq= wherep;
q= (void*)(p-1);
uint8 c= 0;
if(q->byte<ulen)c= ubytes[q->byte];
direction= (1+(q->otherbits|c))>>8;
wherep= q->child+direction;
p= *wherep;
}
/*:18*/
/*19:*/
if(0!=strcmp(u,(const char*)p))return 0;
#if 0
free(p);
#endif
/*:19*/
/*20:*/
if(!whereq){
t->root= 0;
return 1;
}
*whereq= q->child[1-direction];
free(q);
allocated--;
/*:20*/
return 1;
}
/*:16*//*21:*/
static void
traverse(void*top){
/*22:*/
uint8*p= top;
if(1&(intptr_t)p){
critbit0_node*q= (void*)(p-1);
traverse(q->child[0]);
traverse(q->child[1]);
free(q);
allocated--;
}else{
#if 0
free(p);
#endif
}
/*:22*/
}
void critbit0_clear(critbit0_tree*t)
{
if(t->root)traverse(t->root);
t->root= NULL;
}
/*:21*//*23:*/
static int
allprefixed_traverse(uint8*top,
int(*handle)(const char*,void*),void*arg){
/*26:*/
if(1&(intptr_t)top){
critbit0_node*q= (void*)(top-1);
int direction;
for(direction= 0;direction<2;++direction)
switch(allprefixed_traverse(q->child[direction],handle,arg)){
case 1:break;
case 0:return 0;
default:return-1;
}
return 1;
}
/*:26*/
/*27:*/
return handle((const char*)top,arg);/*:27*/
}
int
critbit0_allprefixed(critbit0_tree*t,const char*prefix,
int(*handle)(const char*,void*),void*arg){
const uint8*ubytes= (void*)prefix;
const size_t ulen= strlen(prefix);
uint8*p= t->root;
uint8*top= p;
size_t i;
if(!p)return 1;
/*24:*/
while(1&(intptr_t)p){
critbit0_node*q= (void*)(p-1);
uint8 c= 0;
if(q->byte<ulen)c= ubytes[q->byte];
const int direction= (1+(q->otherbits|c))>>8;
p= q->child[direction];
if(q->byte<ulen)top= p;
}
/*:24*/
/*25:*/
for(i= 0;i<ulen;++i){
if(p[i]!=ubytes[i])return 1;
}
/*:25*/
return allprefixed_traverse(top,handle,arg);
}
/*:23*/
/* end critbit */
/* Nanoseconds per operation */
static size_t normalize(const struct timeval *start,
const struct timeval *stop,
unsigned int num)
{
struct timeval diff;
timersub(stop, start, &diff);
/* Floating point is more accurate here. */
return (double)(diff.tv_sec * 1000000 + diff.tv_usec)
/ num * 1000;
}
int main(int argc, char *argv[])
{
size_t i, j, num;
struct timeval start, stop;
critbit0_tree ct;
char **words, **misswords;
words = strsplit(NULL, grab_file(NULL,
argv[1] ? argv[1] : "/usr/share/dict/words",
NULL), "\n");
ct.root = NULL;
num = talloc_array_length(words) - 1;
printf("%zu words\n", num);
/* Append and prepend last char for miss testing. */
misswords = talloc_array(words, char *, num);
for (i = 0; i < num; i++) {
char lastc;
if (strlen(words[i]))
lastc = words[i][strlen(words[i])-1];
else
lastc = 'z';
misswords[i] = talloc_asprintf(misswords, "%c%s%c%c",
lastc, words[i], lastc, lastc);
}
printf("#01: Initial insert: ");
fflush(stdout);
start = time_now();
for (i = 0; i < num; i++)
critbit0_insert(&ct, words[i]);
stop = time_now();
printf(" %zu ns\n", normalize(&start, &stop, num));
printf("Nodes allocated: %zu (%zu bytes)\n",
allocated, allocated * sizeof(critbit0_node));
printf("#02: Initial lookup (match): ");
fflush(stdout);
start = time_now();
for (i = 0; i < num; i++)
if (!critbit0_contains(&ct, words[i]))
abort();
stop = time_now();
printf(" %zu ns\n", normalize(&start, &stop, num));
printf("#03: Initial lookup (miss): ");
fflush(stdout);
start = time_now();
for (i = 0; i < num; i++) {
if (critbit0_contains(&ct, misswords[i]))
abort();
}
stop = time_now();
printf(" %zu ns\n", normalize(&start, &stop, num));
/* Lookups in order are very cache-friendly for judy; try random */
printf("#04: Initial lookup (random): ");
fflush(stdout);
start = time_now();
for (i = 0, j = 0; i < num; i++, j = (j + 10007) % num)
if (!critbit0_contains(&ct, words[j]))
abort();
stop = time_now();
printf(" %zu ns\n", normalize(&start, &stop, num));
printf("#05: Initial delete all: ");
fflush(stdout);
start = time_now();
for (i = 0; i < num; i++)
if (!critbit0_delete(&ct, words[i]))
abort();
stop = time_now();
printf(" %zu ns\n", normalize(&start, &stop, num));
printf("#06: Initial re-inserting: ");
fflush(stdout);
start = time_now();
for (i = 0; i < num; i++)
critbit0_insert(&ct, words[i]);
stop = time_now();
printf(" %zu ns\n", normalize(&start, &stop, num));
printf("#07: Deleting first half: ");
fflush(stdout);
start = time_now();
for (i = 0; i < num; i+=2)
if (!critbit0_delete(&ct, words[i]))
abort();
stop = time_now();
printf(" %zu ns\n", normalize(&start, &stop, num));
printf("#08: Adding (a different) half: ");
fflush(stdout);
start = time_now();
for (i = 0; i < num; i+=2)
critbit0_insert(&ct, misswords[i]);
stop = time_now();
printf(" %zu ns\n", normalize(&start, &stop, num));
printf("#09: Lookup after half-change (match): ");
fflush(stdout);
start = time_now();
for (i = 1; i < num; i+=2)
if (!critbit0_contains(&ct, words[i]))
abort();
for (i = 0; i < num; i+=2) {
if (!critbit0_contains(&ct, misswords[i]))
abort();
}
stop = time_now();
printf(" %zu ns\n", normalize(&start, &stop, num));
printf("#10: Lookup after half-change (miss): ");
fflush(stdout);
start = time_now();
for (i = 0; i < num; i+=2)
if (critbit0_contains(&ct, words[i]))
abort();
for (i = 1; i < num; i+=2) {
if (critbit0_contains(&ct, misswords[i]))
abort();
}
stop = time_now();
printf(" %zu ns\n", normalize(&start, &stop, num));
/* Hashtables with delete markers can fill with markers over time.
* so do some changes to see how it operates in long-term. */
printf("#11: Churn 1: ");
start = time_now();
for (j = 0; j < num; j+=2) {
if (!critbit0_delete(&ct, misswords[j]))
abort();
if (critbit0_insert(&ct, words[j]) != 2)
abort();
}
stop = time_now();
printf(" %zu ns\n", normalize(&start, &stop, num));
printf("#12: Churn 2: ");
start = time_now();
for (j = 1; j < num; j+=2) {
if (!critbit0_delete(&ct, words[j]))
abort();
if (critbit0_insert(&ct, misswords[j]) != 2)
abort();
}
stop = time_now();
printf(" %zu ns\n", normalize(&start, &stop, num));
printf("#13: Churn 3: ");
start = time_now();
for (j = 1; j < num; j+=2) {
if (!critbit0_delete(&ct, misswords[j]))
abort();
if (critbit0_insert(&ct, words[j]) != 2)
abort();
}
stop = time_now();
printf(" %zu ns\n", normalize(&start, &stop, num));
/* Now it's back to normal... */
printf("#14: Post-Churn lookup (match): ");
fflush(stdout);
start = time_now();
for (i = 0; i < num; i++)
if (!critbit0_contains(&ct, words[i]))
abort();
stop = time_now();
printf(" %zu ns\n", normalize(&start, &stop, num));
printf("#15: Post-Churn lookup (miss): ");
fflush(stdout);
start = time_now();
for (i = 0; i < num; i++) {
if (critbit0_contains(&ct, misswords[i]))
abort();
}
stop = time_now();
printf(" %zu ns\n", normalize(&start, &stop, num));
/* Lookups in order are very cache-friendly for judy; try random */
printf("#16: Post-Churn lookup (random): ");
fflush(stdout);
start = time_now();
for (i = 0, j = 0; i < num; i++, j = (j + 10007) % num)
if (!critbit0_contains(&ct, words[j]))
abort();
stop = time_now();
printf(" %zu ns\n", normalize(&start, &stop, num));
return 0;
}