Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
L
libvmod-selector
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
uplex-varnish
libvmod-selector
Commits
5f4917be
Commit
5f4917be
authored
Mar 03, 2020
by
Geoff Simmons
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add a benchmark utility for the QP functions.
parent
bb903e03
Changes
2
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
463 additions
and
1 deletion
+463
-1
Makefile.am
src/tests/bench/Makefile.am
+5
-1
bench_qp.c
src/tests/bench/bench_qp.c
+458
-0
No files found.
src/tests/bench/Makefile.am
View file @
5f4917be
...
...
@@ -4,8 +4,12 @@ AM_CFLAGS = $(VARNISHAPI_CFLAGS) -I$(top_srcdir)/src -Wall -Werror -Wextra \
-std
=
c99
AM_LDFLAGS
=
$(VARNISHAPI_LIBS)
bin_PROGRAMS
=
bench
bin_PROGRAMS
=
bench
bench_qp
bench_SOURCES
=
bench.c
bench_LDADD
=
$(top_srcdir)
/src/.libs/patricia.o
bench_qp_SOURCES
=
bench_qp.c
bench_qp_LDADD
=
$(top_srcdir)
/src/.libs/qp.o
src/tests/bench/bench_qp.c
0 → 100644
View file @
5f4917be
/*-
* Copyright (c) 2020 UPLEX Nils Goroll Systemoptimierung
* All rights reserved
*
* Author: Geoffrey Simmons <geoffrey.simmons@uplex.de>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/* for strdup() and getline() */
#define _POSIX_C_SOURCE 200809L
/* for srand48() and drand48() */
#define _XOPEN_SOURCE
#include "config.h"
#include <stdio.h>
#include <errno.h>
#include <limits.h>
#include <stdint.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>
#include <sys/resource.h>
#include "vdef.h"
#include "vas.h"
#include "miniobj.h"
#include "vsb.h"
#include "qp.h"
#define BILLION (1000 * 1000 * 1000)
#define ITERATIONS (1000)
#define CLOCK (CLOCK_MONOTONIC)
static
struct
pt_y
*
origo
=
NULL
;
static
uint64_t
tdiff
(
struct
timespec
*
before
,
struct
timespec
*
after
)
{
uint64_t
diff
;
if
(
after
->
tv_nsec
-
before
->
tv_nsec
<
0
)
{
diff
=
(
after
->
tv_sec
-
before
->
tv_sec
-
1
)
*
BILLION
;
diff
+=
BILLION
+
after
->
tv_nsec
-
before
->
tv_nsec
;
return
diff
;
}
diff
=
(
after
->
tv_sec
-
before
->
tv_sec
)
*
BILLION
;
diff
+=
after
->
tv_nsec
-
before
->
tv_nsec
;
return
diff
;
}
static
int
rnd
(
int
lo
,
int
hi
)
{
return
((
int
)(
drand48
()
*
(
hi
-
lo
))
+
lo
);
}
static
inline
void
shuffle
(
unsigned
*
index
,
size_t
n
)
{
for
(
unsigned
i
=
0
;
i
<
n
-
1
;
i
++
)
{
int
j
=
rnd
(
i
,
n
);
int
temp
=
index
[
i
];
index
[
i
]
=
index
[
j
];
index
[
j
]
=
temp
;
}
}
static
void
free_on_exit
(
void
)
{
QP_Free
(
origo
);
}
void
usage
(
const
char
*
argv
,
int
status
)
{
fprintf
(
stderr
,
"Usage: %s [-hs] [-c csvfile] [-d dumpfile] [-i inputfile]
\n
"
" [-m m|p] [-n iterations] [file]
\n
"
,
argv
);
exit
(
status
);
}
int
main
(
int
argc
,
char
*
argv
[])
{
FILE
*
stringsf
=
stdin
,
*
csv
=
NULL
;
char
**
strings
=
NULL
,
**
inputs
=
NULL
,
*
line
,
*
inputf
=
NULL
,
*
csvf
=
NULL
,
*
dumpf
=
NULL
;
size_t
lineln
=
LINE_MAX
;
ssize_t
readln
;
unsigned
n
=
0
,
ninputs
=
0
;
struct
timespec
before
,
after
,
start
,
finish
;
uint64_t
ns
=
0
,
iters
,
matches
,
exacts
,
misses
;
struct
qp_stats
stats
=
{
.
magic
=
QP_STATS_MAGIC
};
int
opt
,
do_shuf
=
0
,
do_iters
=
ITERATIONS
,
do_match
=
1
,
do_prefix
=
1
;
struct
rusage
rusage
;
while
((
opt
=
getopt
(
argc
,
argv
,
"hsc:d:i:m:n:"
))
!=
-
1
)
{
switch
(
opt
)
{
case
'c'
:
csvf
=
optarg
;
break
;
case
'd'
:
dumpf
=
optarg
;
break
;
case
'h'
:
usage
(
argv
[
0
],
EXIT_SUCCESS
);
break
;
case
'i'
:
inputf
=
optarg
;
break
;
case
'm'
:
if
(
strcmp
(
optarg
,
"m"
)
==
0
)
do_prefix
=
0
;
else
if
(
strcmp
(
optarg
,
"p"
)
==
0
)
do_match
=
0
;
else
usage
(
argv
[
0
],
EXIT_FAILURE
);
break
;
case
'n'
:
do_iters
=
atoi
(
optarg
);
break
;
case
's'
:
do_shuf
=
1
;
break
;
default:
usage
(
argv
[
0
],
EXIT_FAILURE
);
}
}
if
(
argc
-
optind
>
1
)
usage
(
argv
[
0
],
EXIT_FAILURE
);
if
(
argc
==
optind
+
1
&&
strcmp
(
argv
[
optind
],
"-"
)
!=
0
)
{
errno
=
0
;
if
((
stringsf
=
fopen
(
argv
[
optind
],
"r"
))
==
NULL
)
{
fprintf
(
stderr
,
"Cannot open %s: %s
\n
"
,
argv
[
optind
],
strerror
(
errno
));
exit
(
EXIT_FAILURE
);
}
}
line
=
malloc
(
lineln
);
AN
(
line
);
errno
=
0
;
while
((
readln
=
getline
(
&
line
,
&
lineln
,
stringsf
))
!=
-
1
)
{
if
(
readln
-
1
>
USHRT_MAX
)
{
fprintf
(
stderr
,
"String length %zd too long (max %d)
\n
"
,
readln
-
1
,
USHRT_MAX
);
exit
(
EXIT_FAILURE
);
}
n
++
;
strings
=
realloc
(
strings
,
n
*
sizeof
(
char
*
));
AN
(
strings
);
line
[
readln
-
1
]
=
'\0'
;
strings
[
n
-
1
]
=
strdup
(
line
);
AN
(
strings
[
n
-
1
]);
}
if
(
ferror
(
stringsf
))
{
fprintf
(
stderr
,
"Error reading %s: %s"
,
stringsf
==
stdin
?
"stdin"
:
argv
[
optind
],
strerror
(
errno
));
exit
(
EXIT_FAILURE
);
}
errno
=
0
;
if
(
stringsf
!=
stdin
&&
fclose
(
stringsf
)
!=
0
)
{
fprintf
(
stderr
,
"Error closing %s: %s
\n
"
,
argv
[
optind
],
strerror
(
errno
));
exit
(
EXIT_FAILURE
);
}
printf
(
"%u strings read
\n
"
,
n
);
if
(
n
==
0
)
exit
(
EXIT_SUCCESS
);
if
(
inputf
!=
NULL
)
{
FILE
*
inf
;
errno
=
0
;
if
((
inf
=
fopen
(
inputf
,
"r"
))
==
NULL
)
{
fprintf
(
stderr
,
"Cannot open %s: %s
\n
"
,
inputf
,
strerror
(
errno
));
exit
(
EXIT_FAILURE
);
}
while
((
readln
=
getline
(
&
line
,
&
lineln
,
inf
))
!=
-
1
)
{
if
(
readln
-
1
>
USHRT_MAX
)
{
fprintf
(
stderr
,
"%s: string length %zd "
"too long (max %d)
\n
"
,
inputf
,
readln
-
1
,
USHRT_MAX
);
exit
(
EXIT_FAILURE
);
}
ninputs
++
;
inputs
=
realloc
(
inputs
,
ninputs
*
sizeof
(
char
*
));
AN
(
inputs
);
line
[
readln
-
1
]
=
'\0'
;
inputs
[
ninputs
-
1
]
=
strdup
(
line
);
AN
(
inputs
[
ninputs
-
1
]);
}
if
(
ferror
(
inf
))
{
fprintf
(
stderr
,
"Error reading %s: %s"
,
inputf
,
strerror
(
errno
));
exit
(
EXIT_FAILURE
);
}
errno
=
0
;
if
(
fclose
(
inf
)
!=
0
)
{
fprintf
(
stderr
,
"Error closing %s: %s
\n
"
,
inputf
,
strerror
(
errno
));
exit
(
EXIT_FAILURE
);
}
printf
(
"%u input strings read
\n
"
,
ninputs
);
}
if
(
inputf
==
NULL
||
ninputs
==
0
)
{
printf
(
"Using string set as inputs
\n
"
);
inputs
=
strings
;
ninputs
=
n
;
}
free
(
line
);
if
(
csvf
!=
NULL
)
{
errno
=
0
;
if
((
csv
=
fopen
(
csvf
,
"w"
))
==
NULL
)
{
fprintf
(
stderr
,
"Cannot open %s: %s
\n
"
,
csvf
,
strerror
(
errno
));
exit
(
EXIT_FAILURE
);
}
fprintf
(
csv
,
"type,matches,exact,t
\n
"
);
}
AZ
(
clock_gettime
(
CLOCK_REALTIME
,
&
before
));
srand48
(
before
.
tv_nsec
);
AZ
(
clock_getres
(
CLOCK
,
&
before
));
printf
(
"Clock resolution %ld ns
\n
"
,
before
.
tv_sec
*
BILLION
+
before
.
tv_nsec
);
printf
(
"Building trie ...
\n
"
);
for
(
unsigned
i
=
0
;
i
<
n
;
i
++
)
{
int
ret
;
errno
=
0
;
(
void
)
clock_gettime
(
CLOCK
,
&
before
);
ret
=
QP_Insert
(
&
origo
,
i
,
strings
);
(
void
)
clock_gettime
(
CLOCK
,
&
after
);
if
(
ret
!=
0
)
{
fprintf
(
stderr
,
"QP_Insert() failed: %s
\n
"
,
strerror
(
errno
));
exit
(
EXIT_FAILURE
);
}
ns
+=
tdiff
(
&
before
,
&
after
);
}
AZ
(
atexit
(
free_on_exit
));
printf
(
"... complete.
\n
"
);
printf
(
"%u strings inserted in %.9f s, mean %lu ns/insert
\n
"
,
n
,
ns
*
1e-9
,
ns
/
n
);
if
(
dumpf
!=
NULL
)
{
FILE
*
df
;
struct
vsb
*
vsb
;
printf
(
"
\n
Dumping trie to %s ...
\n
"
,
dumpf
);
vsb
=
QP_Dump
(
origo
,
strings
);
CHECK_OBJ_NOTNULL
(
vsb
,
VSB_MAGIC
);
errno
=
0
;
if
((
df
=
fopen
(
dumpf
,
"w"
))
==
NULL
)
{
fprintf
(
stderr
,
"Cannot open %s: %s
\n
"
,
dumpf
,
strerror
(
errno
));
exit
(
EXIT_FAILURE
);
}
if
(
fwrite
(
VSB_data
(
vsb
),
1
,
VSB_len
(
vsb
),
df
)
!=
(
unsigned
)
VSB_len
(
vsb
))
{
fprintf
(
stderr
,
"Error writing to %s: %s
\n
"
,
dumpf
,
strerror
(
errno
));
exit
(
EXIT_FAILURE
);
}
VSB_destroy
(
&
vsb
);
if
(
fclose
(
df
)
!=
0
)
{
fprintf
(
stderr
,
"Error closing %s: %s
\n
"
,
dumpf
,
strerror
(
errno
));
exit
(
EXIT_FAILURE
);
}
printf
(
"... done.
\n
"
);
}
printf
(
"
\n
Getting stats ...
\n
"
);
(
void
)
clock_gettime
(
CLOCK
,
&
before
);
QP_Stats
(
origo
,
strings
,
&
stats
);
(
void
)
clock_gettime
(
CLOCK
,
&
after
);
printf
(
"Stats computed in %lu ns
\n
"
,
tdiff
(
&
before
,
&
after
));
printf
(
"%lu nodes
\n
"
,
stats
.
nodes
);
printf
(
"%lu leaves
\n
"
,
stats
.
leaves
);
printf
(
"%lu terminal nodes
\n
"
,
stats
.
terms
);
printf
(
"%lu bytes node size
\n
"
,
stats
.
nodesz
);
printf
(
"%lu min terminating node depth
\n
"
,
stats
.
dmin
);
printf
(
"%lu max terminating node depth
\n
"
,
stats
.
dmax
);
printf
(
"%.1f mean terminating node depth
\n
"
,
stats
.
davg
);
printf
(
"%lu min fanout
\n
"
,
stats
.
fmin
);
printf
(
"%lu max fanout
\n
"
,
stats
.
fmax
);
printf
(
"%.1f mean fanout at non-terminating nodes
\n
"
,
stats
.
favg
);
if
(
do_iters
==
0
)
exit
(
EXIT_SUCCESS
);
unsigned
index
[
ninputs
];
for
(
unsigned
i
=
0
;
i
<
ninputs
;
i
++
)
index
[
i
]
=
i
;
printf
(
"
\n
Benchmarking with %d iterations of %u input strings
\n
"
,
do_iters
,
ninputs
);
if
(
do_shuf
)
printf
(
"Shuffling inputs on each iteration
\n
"
);
(
void
)
clock_gettime
(
CLOCK
,
&
start
);
if
(
!
do_match
)
goto
prefix
;
printf
(
"
\n
Benchmarking matches ...
\n
"
);
ns
=
0
;
iters
=
0
;
matches
=
0
;
for
(
int
iter
=
0
;
iter
<
do_iters
;
iter
++
)
{
if
(
do_shuf
)
shuffle
(
index
,
ninputs
);
for
(
unsigned
i
=
0
;
i
<
ninputs
;
i
++
)
{
unsigned
idx
,
stridx
=
index
[
i
];
uint64_t
diff
;
(
void
)
clock_gettime
(
CLOCK
,
&
before
);
idx
=
QP_Lookup
(
origo
,
strings
,
inputs
[
stridx
]);
(
void
)
clock_gettime
(
CLOCK
,
&
after
);
if
(
idx
!=
UINT_MAX
)
{
AZ
(
strcmp
(
strings
[
idx
],
inputs
[
stridx
]));
matches
++
;
}
diff
=
tdiff
(
&
before
,
&
after
);
ns
+=
diff
;
iters
++
;
if
(
csv
!=
NULL
)
{
if
(
idx
!=
UINT_MAX
)
fprintf
(
csv
,
"match,1,1,%lu
\n
"
,
diff
);
else
fprintf
(
csv
,
"match,0,0,%lu
\n
"
,
diff
);
}
}
}
assert
(
matches
<=
iters
);
printf
(
"... complete.
\n
"
);
printf
(
"%ld match operations in %.09f s, mean %lu ns/op
\n
"
,
iters
,
ns
*
1e-9
,
ns
/
iters
);
printf
(
"%ld matches, %ld misses
\n
"
,
matches
,
iters
-
matches
);
prefix:
if
(
!
do_prefix
)
goto
finish
;
printf
(
"
\n
Benchmarking prefix matches ...
\n
"
);
unsigned
*
indices
=
malloc
(
ninputs
*
sizeof
(
*
indices
));
struct
match_data
match
=
{
.
magic
=
MATCH_DATA_MAGIC
,
.
indices
=
indices
,
.
limit
=
ninputs
,
};
ns
=
0
;
iters
=
0
;
matches
=
0
;
misses
=
0
;
exacts
=
0
;
for
(
int
iter
=
0
;
iter
<
do_iters
;
iter
++
)
{
if
(
do_shuf
)
shuffle
(
index
,
ninputs
);
for
(
unsigned
i
=
0
;
i
<
ninputs
;
i
++
)
{
unsigned
stridx
=
index
[
i
];
int
ret
;
uint64_t
diff
;
(
void
)
clock_gettime
(
CLOCK
,
&
before
);
ret
=
QP_Prefixes
(
origo
,
strings
,
inputs
[
stridx
],
&
match
);
(
void
)
clock_gettime
(
CLOCK
,
&
after
);
AZ
(
ret
);
if
(
match
.
n
>
0
)
{
assert
(
match
.
min
<=
match
.
max
);
matches
+=
match
.
n
;
}
else
misses
++
;
for
(
unsigned
j
=
0
;
j
<
match
.
n
;
j
++
)
{
assert
(
match
.
indices
[
j
]
>=
match
.
min
);
assert
(
match
.
indices
[
j
]
<=
match
.
max
);
AN
(
strstr
(
inputs
[
stridx
],
strings
[
match
.
indices
[
j
]]));
}
if
(
match
.
exact
!=
UINT_MAX
)
{
AZ
(
strcmp
(
inputs
[
stridx
],
strings
[
match
.
exact
]));
exacts
++
;
}
diff
=
tdiff
(
&
before
,
&
after
);
ns
+=
diff
;
iters
++
;
if
(
csv
!=
NULL
)
{
if
(
match
.
n
>
0
)
fprintf
(
csv
,
"prefix,%u,%d,%lu
\n
"
,
match
.
n
,
match
.
exact
!=
UINT_MAX
,
diff
);
else
fprintf
(
csv
,
"prefix,0,0,%lu
\n
"
,
diff
);
}
}
}
AZ
(
getrusage
(
RUSAGE_SELF
,
&
rusage
));
assert
(
matches
>=
exacts
);
printf
(
"... complete.
\n
"
);
printf
(
"%ld prefix match operations in %.09f s, mean %lu ns/op
\n
"
,
iters
,
ns
*
1e-9
,
ns
/
iters
);
printf
(
"%ld prefixes found, %ld exact matches, %ld misses
\n
"
,
matches
,
exacts
,
misses
);
finish:
(
void
)
clock_gettime
(
CLOCK
,
&
finish
);
if
(
csv
!=
NULL
&&
fclose
(
csv
)
!=
0
)
{
fprintf
(
stderr
,
"Error closing %s: %s
\n
"
,
csvf
,
strerror
(
errno
));
exit
(
EXIT_FAILURE
);
}
ns
=
tdiff
(
&
start
,
&
finish
);
printf
(
"
\n
Benchmark wall clock time %.09f s
\n
"
,
ns
*
1e-9
);
AZ
(
getrusage
(
RUSAGE_SELF
,
&
rusage
));
printf
(
"user %.06f s, sys %.06f s, vcsw %ld, ivcsw %ld
\n
"
,
rusage
.
ru_utime
.
tv_sec
+
1e-6
*
rusage
.
ru_utime
.
tv_usec
,
rusage
.
ru_stime
.
tv_sec
+
1e-6
*
rusage
.
ru_stime
.
tv_usec
,
rusage
.
ru_nvcsw
,
rusage
.
ru_nivcsw
);
exit
(
EXIT_SUCCESS
);
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment