- Installing OpenOnload
- Using wrk HTTPD loading
- Using redis-benchmark
- Using nats-bench
- Using libmemcached memaslap
After building Rai DS, the scripts used to start ds_server will also start
kv_server with default parameters to create the memory segment to attach to,
if it is not already started.
Many of these benchmarks use multiple ports for the same data store. It is
possible to listen on the same port with multiple instances and use the kernel
load balancing with SO_REUSEPORT and also the OpenOnload bypass load
balancing with EV_CLUSTER_SIZE, but I found these are not as mature as using
multiple TCP ports. The IP addresses used below are these directly connected
hosts:
client 1 (i9 10980xe), port 1 : 192.168.25.21 <--> server (3970x), port 1 : 192.168.25.22
client 2 (i9 10980xe), port 1 : 192.168.26.91 <--> server (3970x), port 2 : 192.168.26.22
The method used to install the Solarflare OpenOnload driver, which includes
a LD_PRELOAD userspace library TCP stack which masquerades as a socket
interface. And also includes Linux Kernel modules, one for the Solarflare
network card (sfc.ko) and another for the Onload component (onload.ko).
These can be built using the rpmbuild command by downloading the
OpenOnload from the Solarflare
download area:
$ sudo dnf install rpm-build make gcc-c++ kernel-devel kernel-headers
$ rpmbuild --rebuild onload-7.1.0.265-1.src.rpm
$ sudo dnf install rpmbuild/RPMS/x86_64/onload-*.rpmOn the server, the shell script start_www.sh will run multiple
instances the ds_server using taskset to bind to separate CPUs on
incrementing ports, optionally using the onload accelerator. On the
client(s), the shell script start_wrk.sh will run multiple
wrk instances using taskset and optionally onload to connect to
incrementing ports on the server. The results are collected by capturing the
output of wrk on the clients after running the scripts several times, each
time to scale the number of threads needed.
Clone the wrk repository and build it and copy it to the client machines,
it does not require configuration.
$ cd raids
$ git clone https://github.com/wg/wrk
$ cd wrk ; make ; cd ..The method used for this is to create a network of clients and servers, each communicating on their own port, tied to their own CPU. Here is a 4 CPU configuration:
wrk connect 48080 -- net interface 1 -- ds_server listening port 48080
wrk connect 48081 -- net interface 1 -- ds_server listening port 48081
wrk connect 48082 -- net interface 2 -- ds_server listening port 48082
wrk connect 48083 -- net interface 2 -- ds_server listening port 48083
Each wrk instance is a separate program which can be on different hosts. The
server uses the same shared memory segment, so the operations performed will be
seen in all instances, even though they are different processes on listening on
different ports. The following is using 2 client hosts for each of the
interface ports, but it is possible to use more or less hosts, and combine
the statistics output by the wrk load driver.
Run 4 ds_server instances on port 48080 ... 48083 for the kernel networking
test (without Kernel bypass):
[server]$ sh graph/start_www.sh 48080 4
Starting instance on CPU 1 on port 48080
. . . (cut for brevity)
Starting instance on CPU 2 on port 48081
. . .
Starting instance on CPU 3 on port 48082
. . .
Starting instance on CPU 4 on port 48083
. . .
<html><body> Created </body></html>
waiting for ctr-c to stop 4 instancesAfter the last instance is run, the script creates the index.html page by
using the curl -X POST command. Run 2 wrk instances on client 1 and 2
instances on client 2. The instances will run for 60 seconds.
[client1]$ sh graph/start_wrk.sh 192.168.25.22 48080 2
Starting instance on CPU 1 to http://192.168.25.22:48080/index.html
Starting instance on CPU 2 to http://192.168.25.22:48081/index.html
svrpids = 3764145 3764148
waiting for 2 wrk instances, run time 60s
wrk pid 3764145 exit status 0
wrk pid 3764148 exit status 0
Requests/sec: 365188.85
complete[client2]$ sh graph/start_wrk.sh 192.168.26.22 48082 2
Starting instance on CPU 1 to http://192.168.26.22:48082/index.html
Starting instance on CPU 2 to http://192.168.26.22:48083/index.html
svrpids = 24296 24299
waiting for 2 wrk instances, run time 60s
wrk pid 24296 exit status 0
wrk pid 24299 exit status 0
Requests/sec: 388122.32
completeThe IP addresses used above will be the addresses assigned to the server. The last digits of the scripts, a 48080 4 on the server and 48080 2, 48082 2 on the clients, tell the scripts how many instances to run and which ports to use. Ideally, you want to start the clients at the same time in order to load the server instances simultaneously. Stop the servers by using 'ctrl-c' on the console running them. Repeat the above sequences, starting more servers and more clients until you run out of CPUs available.
To use the Kernel Bypass with OpenOnload, repeat the above again with the
USE_ONLOAD=yes environment variable set in the consoles of both the server
and the client machines. The start_www.sh and start_wrk.sh scripts both
look for $USE_ONLOAD and start the instances with the onload command when
it is set to yes.
After running the tests, there will be files in the working directory with
results in a format that can be combined and graphed: wrk_results.kernel and
wrk_results.onload, if onload is used. These are the sums of the
requests/sec output by the wrk instances. Both clients can be combined
together in a single file using the merge2.sh script:
[server]$ scp client1:~/raids/wrk_results.kernel wrk_results.kernel.1
[server]$ scp client2:~/raids/wrk_results.kernel wrk_results.kernel.2
[server]$ sh graph/merge2.sh wrk_results.kernel.1 wrk_results.kernel.2 > wrk_results.kernelThe merge script is stupid, it does not match the lines up in any way, it just sums the two numbers and prints them to stdout. If there are mismatched lines from client failures or unequal runs, the data may not be be useful.
The files wrk_results.{kernel,onload} should be graphable using the gnuplot command:
[server]$ gnuplot-qt -p graph/plot_www.gnulotIncluded in the graph directory is a patch for redis-benchmark.c that causes
it to stop processing when it receives a HUP signal. The allows for more
accurate rate reporting. Without this patch, it will continue until it
finishes the number of requests, which causes some processes to finish before
others skewing the rate higher when processing close to the limit of the
network bandwidth. Patch redis-benchmark.c and compile it:
$ git clone https://github.com/redis/redis
$ cd redis/src
$ patch < ~/raids/graph/redis-benchmark.c-diff
$ cd ..
$ make redis-benchmark
$ mkdir -p ~/bin ; cp src/redis-benchmark ~/bin
$ PATH=~/bin:$PATH ; export PATHTo create the barchart of the single threaded redis-benchmark results, start a redis server
using the start_ds_server.sh command, which will check the USE_ONLOAD variable and can
start multiple copies running on different ports.
# set to yes for onload tcp or unset it for kernel tcp
[server]$ unset USE_ONLOAD
[server]$ sh graph/start_ds_server.sh 6379 1 redisCapture localhost.
[server]$ redis-benchmark -h 127.0.0.1 -p 6379 -c 50 -P 128 -n 100000 --csv > rb_s128.localhost.csvCapture kernel from a client machine.
[client]$ redis-benchmark -h 192.168.25.22 -p 6379 -c 50 -P 128 -n 100000 --csv > rb_s128.kernel.csv
[client]$ scp rb_s128.kernel.csv server:~/raids/Restart the server with onload.
[server]$ USE_ONLOAD=yes ; export USE_ONLOAD
[server]$ sh graph/start_ds_server.sh 6379 1 redisCapture onload tcp from a client machine.
[client]$ onload '--profile=latency' taskset -c 1 redis-benchmark -h 192.168.25.22 -p 6379 -c 50 -P 128 -n 100000 --csv > rb_s128.onload.csv
[client]$ scp rb_s128.onload.csv server:~/raids/The csv files can be converted from csv format to gnuplot format using the
redis_histogram.sh shell script. The files
rb_s128.{localhost,kernel,onload} should be graphable using this gnuplot
command, which reads them from the current directory:
[server]$ sh graph/redis_histogram.sh rb_s128.localhost.csv > rb_s128.localhost
[server]$ sh graph/redis_histogram.sh rb_s128.kernel.csv > rb_s128.kernel
[server]$ sh graph/redis_histogram.sh rb_s128.onload.csv > rb_s128.onload
[server]$ gnuplot-qt -p graph/plot_rb_s128.gnuplotTo create the pipeline graphs for latency, these commands will do. Start a
ds_server redis instance.
# set to yes for onload tcp or unset it for kernel tcp
[server]$ unset USE_ONLOAD
[server]$ sh graph/start_ds_server.sh 6379 1 redisThe clients are driven by a shell script, since the redis-benchmark commands
are run 200 times with different pipeline counts. These are echoed to stdout
and also output to a files called rb_slat.{localhost,kernel,onload},
depending on the USE_ONLOAD state and the address equal to 127.0.0.1
[server]$ sh graph/rb_slat.sh 127.0.0.1 200The the clients, one with kernel, the other with onload.
[client]$ unset USE_ONLOAD
[client]$ sh graph/rb_slat.sh 192.168.25.22 200
[client]$ scp rb_slat.kernel server:~/raids/[server]$ USE_ONLOAD=yes ; export USE_ONLOAD
[server]$ sh graph/start_ds_server.sh 6379 1 redis[client]$ USE_ONLOAD=yes ; export USE_ONLOAD
[client]$ sh graph/rb_slat.sh 192.168.25.22 200
[client]$ scp rb_slat.onload server:~/raids/The plot commands, one for the rate graph and the other for the average latency
graph. Both read the rb_slat.{localhost,kernel,onload} files in the working
directory.
[server]$ gnuplot-qt -p graph/plot_rb_srat.gnuplot
[server]$ gnuplot-qt -p graph/plot_rb_slat.gnuplotThe GET scaling graphs are generated with a ds_server started on each
port, like wrk above, with the servers scaled from 2 to 36. This requires
repeatedly the scripts to collect the data by changing the 4 below and the
2 on the client scripts. The repeating the same thing again with
USE_ONLOAD=yes in the environment to generate the OpenOnload results.
[server]$ sh graph/start_ds_server.sh 6379 4 redis
Starting instance on CPU 1 on port 6379
. . . (cut for brevity)
Starting instance on CPU 2 on port 6380
. . .
Starting instance on CPU 3 on port 6381
. . .
Starting instance on CPU 4 on port 6382
. . .
waiting for ctr-c to stop 4 instancesThe clients on each of the machines connected to the ports are started by the
start_rb.sh script. It takes the address, the port, the number of
redis-benchmark instances to run, the pipeline size, and the data size, which
uses 3 and 128 in the two graphs.
[client1]$ sh graph/start_rb.sh
usage: graph/start_rb.sh address baseport NCPU pipesize datasize
where NCPU is the number of redis-benchmark instances to run
[client1]$ sh graph/start_rb.sh 192.168.25.22 6379 2 128 128
Starting instance on CPU 1 to connect to port 6379
Starting instance on CPU 2 to connect to port 6380
svrpids = 76676 76701
running 2 instances for 30 seconds
Stopping instance on CPU 1 port 6385 pid 76676
kill -HUP 76676
Stopping instance on CPU 2 port 6386 pid 76701
kill -HUP 76701
pid 76676 exit status 0
pid 76701 exit status 0
Requests/sec: 6873308.75
complete[client2]$ sh graph/start_rb.sh 192.168.26.22 6381 2 128 128
Starting instance on CPU 1 to connect to port 6381
Starting instance on CPU 2 to connect to port 6382
svrpids = 131080 131107
running 2 instances for 30 seconds
Stopping instance on CPU 1 port 6381 pid 131080
kill -HUP 131080
Stopping instance on CPU 2 port 6382 pid 131107
kill -HUP 131107
Requests/sec: 6962871.50
complete
[client2]$ cat redis_get_p128_d128.kernel
1 3517107.50
2 6962871.50The requests/sec are capture in a file called redis_get_p128_d128.kernel and
redis_get_p128_d128.onload, depending on whether USE_ONLOAD=yes is in the
environment. To graph these files, use the merge2.sh and
plot_redis_get_d128.gnuplot scripts:
[server]$ scp client1:~/raids/redis_get_p128_d128.kernel redis_get_p128_d128.kernel.1
[server]$ scp client2:~/raids/redis_get_p128_d128.kernel redis_get_p128_d128.kernel.2
[server]$ sh graph/merge2.sh redis_get_p128_d128.kernel.? > redis_get_p128_d128.kernel
[server]$ gnuplot-qt -p graph/plot_redis_get_d128.gnuplotRepeat the above with different cores, different pipelines, different
datasizes, setting USE_ONLOAD=yes, to construct these data files. The
client1 and client2 examples used above are run simultaneously so that the
merging of the two data files represents concurrent execution.
Install libmemcached, which includes memaslap:
$ dnf install libmemcached