The picoquicdemo program supports multiple applications, one of which is “quic perf” defined by Nick Banks in this draft. To quote from the draft, The QUIC performance protocol provides a simple, general-purpose protocol for testing the performance characteristics of a QUIC implementation.
The original Quic Perf protocol was very simple. The client opens QUIC connection with the LPN set to “perf”, and then it opens bidirectional streams. The first 8 bytes sent by the client on each stream encode the size of the data that the server will send on the return stream. This can be used to measure batch performance, simply requesting a large amount of data and measuring how long it takes to get the result. It can also be used to measure transactional applications: open a large number of streams, require a small amount of data on each, and measure how long it takes to process that many query-reponse exchanges.
Recently, we extended this simple protocol to also test “real time” workloads, such as would be generated by “media over QUIC” (MoQ). In the extension, we reserve two specific 4 bytes value:
By opening multiple bidirectional streams, the client can create load patterns that emulate the traffic of an audio/video server.
By default, the picoquic demo server supports the QUIC “perf” protocol. Simply set a connection with ALPN “perf”, and have the client open bidirectional streams for classic batch requests, or for media streams, or for datagrams.
The picoquic demo client can be directed to use the QUIC perf protocol and request batch streams, media streams or datagram streams. The “performance” scenario needs to be specified on the command line, as in:
.\picoquicdemo -a perf test.privateoctopus.com 4433 <scenario description>
Where -a perf means set ALPN to “perf”, and use the quicperf protocol.
The scenario description is composed of a series of stream descriptions, separated by semicolons:
scenario = stream_description | stream_description ';' *scenario
Each stream description contains an order set of parameters, specifying the details of what is expected:
Many of these fields are optional:
The formal syntax is:
scenario = stream_description | stream_description ';' *stream_description
stream_description =
[ '=' id [':' previous-stream-id ':' ]]['*' repeat_count ':']
{ batch_stream_ | media_stream | datagram_stream }
id = alphanumeric-string | '-'
previous-stream-id = alphanumeric-string
batch_stream = post_size ':' response_size
media_stream = 'm' media_description
datagram_stream = 'd' media_description
media_description = frequency ':' [ 'n' nb_frames ':' ] frame_size ':'
[ group_description ':' ] [ first_frame ':'] [ reset_delay ':' ]
group_description = 'G' frames_per_group
first_frame = ['I' first_frame_size ]
reset_delay = ['D' reset_delay_in_ms ]
Examples of scenarios could be:
batch_scenario = "=b1:*1:397:1000000;"
datagram_scenario = "=a1:d50:n250:100;"
media_scenario = "=v1:s30:n150:2000:G30:I20000;"
multimedia_scenario = "=a1:d50:p2:S:n250:80; \
= vlow: s30 :p4:S:n150 : 3750 : G30 : I37500; \
= vmid: s30 :p6:S:n150 : 6250 : G30 : I62500 : D250000; \
= vhi: s30 :p8:S: n150 : 12500 : G150 : I125000 : D250000;"
parallel_multimedia_scenario= "=a1:d50:p2:S:n250:80; \
= vlow:*3:s30 :p4:S:n150 : 3750 : G30 : I37500; \
= vmid:*3:s30 :p6:S:n150 : 6250 : G30 : I62500 : D300000; \
= vhi:*3 : s30 :p8:S: n150 : 12500 : G150 : I125000 : D250000;"
To run the “perf” protocol and run a basic test scenario, do:
.\picoquicdemo -a perf test.privateoctopus.com 4433 "*1:397:5000000;"
When used as a client, the program will display statistics, e.g.:
Connection_duration_sec: 4.425348
Nb_transactions: 10000
Upload_bytes: 1000000
Download_bytes: 1000000
TPS: 2259.709293
Upload_Mbps: 1.807767
Download_Mbps: 1.807767
For more detailed statistics, or for gathering statistics on servers, picoquicdemo
can provide performance logs, see .
There are lots of other arguments in picoquicdemo, but you probably don’t need them for
running quicperf, although you may consider collecting quic logs using the -q option when
debugging. Also, the “-h”
option will produce a list of command line arguments.
.\picoquicdemo -h
When doing performance measurements, the natural instinct is to turn off all logging, because writing logs slows down the program execution. On the other hand, it is very useful to have at least some logging, in order to understand what changes from run to run, and what might affected performance. The performance logs are designed to minimize the interference. The data is written to disk at the end of the connection. If the performance test involves multiple simultaneous connections, the server will keep the data in memory and write it to disk when all connections are complete.
To produce the performance logs with picoquicdemo, use the argument -F as in:
.\picoquicdemo -k key.pem -c cert.pem -p 4433 -F server_log.csv
.\picoquicdemo -q client_log.csv -a perf test.privateoctopus.com 4433 "*1:397:5000000;"
The performance logs are formatted as CSV file, with the following columns:
The standard Perf protocol uses bidirectional streams in a very simple way: the client opens a stream and starts sending data; the server reads the number of required bytes in the first 8 bytes of the client stream, and sends that many bytes to the client. We extend this protocol by using unidirectional streams and datagrams.
The extended Perf protocol also uses bidirectional streams. The first 16 bytes sent by the client encode the type of response expected by the sender. The first 8 bytes use reserved values to differentiate these streams from the standard “batch” stream:
The complete set of 16 bytes is defined as:
media request header {
media or datagram mark (32),
frame size (32),
priority (8),
frequency (8),
number of frames (24),
first frame size (24)
}
Upon receiving a request header, the server will start sending frames as specified by the frequency. If the client requested datagrams, the server will send datagrams as specified by the frequency. The first datagram (frame number 0) will be sent immediately. The other datagrams will be sent at:
datagram_send_time = first_datagram_send_time + frame_number*1_second/frequency
Each datagram will carry a header and a payload, with a combined size set to the requested frame size. (The first frame size parameter is ignored for datagrams.) The first bytes of the datagram contain a header encoded as:
datagram header {
request stream ID (i),
frame number (i),
datagram send time (64)
}
The datagram send time is the local time at the server, encoded in microseconds. When all datagrams have been sent, the server closes the media request stream.
If the client requested a “media” stream, the server will send the requested number of frames on the return side of the bilateral stream that carried the client request. The first frame contains “first frame size” bytes, while the other frames contain “frame size” bytes. The first frame is queued on the stream immediately. The next frames will be queued at:
frame_send_time = first_frame_send_time + frame_number*1_second/frequency
The first 8 bytes of each frame carry the frame_send_time, set at the
local time at which the server queued the frame, expressed in microseconds
and encoded on 64 bits.
The client may issue a stop sending request for a specific media request
stream. Upon receiving the request, the server will reset the stream, without
sending any additional frame.