diff --git a/doc/html/highload.html b/doc/html/highload.html index 63d9934..a643683 100644 --- a/doc/html/highload.html +++ b/doc/html/highload.html @@ -660,36 +660,127 @@ proxy -osTCP_NODELAY=0 -ocTCP_NODELAY=0 On Linux, splice() passes data between the client and the server connections through a kernel pipe, without copying it into the 3proxy process. Linux builds -include it, but a service uses it only when given the -s option: +include it, and services use it by default where it pays; -s sets the mode:
-proxy -s
-socks -s
+proxy -s0
 tcppm -s3 8080 192.168.1.1 80
 
--s alone splices both directions where it pays: an HTTP body of known length if at +-s alone, the default, splices both directions where it pays: an HTTP body of known length if at least 16K of it is left to read, a stream (CONNECT, SOCKS, tcppm) once it carries large reads, going back to copying when they turn small. -s1 always splices what the server sends to the client (downloads), -s2 what the client sends to the server (uploads), -s3 both; -s5, -s6 and -s7 (the same as -s) do it for those directions -only where it pays. -s0, the default, turns it off. In builds without it (CMake option 3PROXY_USE_SPLICE=OFF, +only where it pays. -s0 turns it off. In builds without it (CMake option 3PROXY_USE_SPLICE=OFF, or -DWITHSPLICE removed from the Makefile) -s is accepted and ignored. -

splice() pays off when most of the traffic is large transfers: in the -benchmarks it moved -1.3 to 2.1 times more data than the read/write path at 40-55% less CPU per gigabyte. -It does not help small requests and short connections, which go slower with it, and -every spliced direction needs a pipe (two file descriptors) and more kernel -buffer memory. Data filters are not applied to spliced data. +

splice() pays off when most of the traffic is large transfers. Measured in +the benchmarks +on an 8 CPU KVM virtual machine (Intel Xeon Skylake, 2.0 GHz, Linux 6.12), with +3proxy confined to one CPU and the client and the origin on CPUs and in network +namespaces of their own, linked by veth pairs (MTU 1500), default 16K buffers, +the automatic mode (-s) against copying (-s0): + + + + + + + + + + + +
load-s0-sCPU per gigabyte
TCP streams through tcppm, CONNECT or SOCKS9.4-10.2 Gbit/s28-34 Gbit/s65-70% less
HTTP proxy, 1 MB answers on kept-alive connections9.4 Gbit/s25.3 Gbit/s62% less
2000 clients, 50 KB answers: tcppm11.0k req/s18.1k req/s39% less
2000 clients, 50 KB answers: CONNECT, SOCKS10.4-10.5k req/s15.8k req/s33-35% less
2000 clients, 50 KB answers: HTTP proxy8.1k req/s9.6k req/s16% less
2000 clients, 2 KB POST and 50 KB answer: tcppm10.5k req/s16.4k req/s36% less
2000 clients, 2 KB POST and 50 KB answer: CONNECT, SOCKS9.8-10.1k req/s14.6-14.7k req/s31-34% less
2000 clients, 2 KB POST and 50 KB answer: HTTP proxy7.5k req/s9.4k req/s20% less
1K requests, kept alive or a connection eachwithin 5% either way: the automatic mode does not splice them
+These loads are synthetic: the network between the client, 3proxy and the +origin has no bandwidth limit and no delay, so every connection goes as fast +as the CPUs allow, which favours splice() more than real clients would. The +mix scenarios, many connections with web-like exchanges, over links emulated +as below, are closer to a proxy giving clients access to the Internet. +

The mix closer to a proxy giving clients access to the Internet: 20000 +connections, 3000 exchanges a second, web-like answers (median 12K, up to +8M), as a TCP relay, an HTTP proxy and through CONNECT, on the same machine, +with the clients' links emulated as in the grace delay tests below. 3proxy's +own CPU time per exchange with -s against -s0 (and the CPU's busy time, the +kernel's network work included): + + + + + + +
client linktcppmHTTP proxyCONNECT
no limit, no delay8% less (4%)9% less (6%)12% less (7%)
100 Mbit/s, 2 ms, 0.1% loss7% less (+1%)1% less (+3%)6% less (+1%)
20 Mbit/s, 5 ms, 0.5% loss+1% (+3%)12% less (5%)+7% (+8%)
5 Mbit/s, 25 ms, 1% loss8% less (0%)10% less (9%)6% less (2%)
+

Conclusion: for web traffic -s, the default, and -s0 show no +significant difference: most connections are slow and their exchanges +short, splice() applies to few of them, and the difference stays within +0-12% of 3proxy's CPU at no cost in latency. -s increases the speed and +decreases CPU usage for high-speed large uploads and downloads, 3 times the +speed at 60-70% less CPU per gigabyte here, especially for traffic inside +high-speed local networks and virtual environments, where nothing but the +CPU limits a connection. +

Every spliced direction needs a pipe (two file descriptors) and more kernel +buffer memory. splice() is not used where 3proxy has to see the data: on a connection while +data filters apply to it (an HTTP body is spliced only once the filters are +done with it: they saw it whole, or it is larger than filtermaxsize, which +they skip), and not at all by a service that ends or begins TLS (ssl_serv, +ssl_cli).

TCP_NODELAY and splice() do not conflict and can be combined on high-speed connections.

Add Grace Delay to Reduce System Calls

-
proxy -g8000,3,10
-The first parameter is the average read size we want to keep, the second parameter is -the minimal number of packets in the same direction to apply the algorithm, -and the last value is the delay added after polling and prior to reading data. -The example above adds a 10-millisecond delay before reading data if the average -polling size is below 8000 bytes and 3 read operations have been made in the same -direction. It's especially useful with splice.
logdump 1 1
is useful -to see how grace delays work; choose a delay value to avoid filling the read -buffer (typically 64K) but keep the request sizes close to the chosen average -on large file uploads/downloads. +
proxy -g8000,2,2
+-gTRAFF,NUM,DELAY: once NUM reads in a row in one direction, within the same +second, have averaged TRAFF bytes or less, 3proxy waits DELAY milliseconds +before it polls and reads again, so that data gathers in the socket and is read +in fewer, larger pieces. The example waits 2 milliseconds after two reads in a +row averaging 8000 bytes or less. The counts start again every second and +whenever the other direction is read, and the wait stops as soon as reads grow +larger than TRAFF on average. +

Fewer reads mean fewer system calls, wakeups and context switches: it +decreases CPU usage on connections which carry data in small pieces, such as +bulk transfers to and from clients on slow links. The cost is the throughput of +a single connection. While 3proxy waits, it reads at most a buffer's worth +after each wait: 16K copying (with the default buffer size; less with -b), up +to 64K (the pipe) with splice(). This caps a connection's direction at one +buffer per DELAY: + + + + + + + +
DELAYcopying, 16K per readsplice(), 64K per read
1 ms131 Mbit/s524 Mbit/s
2 ms65 Mbit/s262 Mbit/s
3 ms44 Mbit/s175 Mbit/s
5 ms26 Mbit/s105 Mbit/s
10 ms13 Mbit/s52 Mbit/s
+These are the limits while the wait applies; the wait stops once reads grow +larger than TRAFF, so a fast connection escapes it as soon as data arrives +faster than TRAFF per DELAY. +

Measured on the same Intel machine as splice() above, the clients' links +emulated for each connection (rate limit, delay and loss each way, with fq and +netem on the client's side), 16 streams through tcppm and a mix of 20000 +connections at a fixed 3000 exchanges a second (web-like answers, median 12K), +against 3proxy without grace delay: + + + + + + + + + + + +
client linksetting16 streams: throughput16 streams: 3proxy CPU per gigabytemix, HTTP proxy: CPU busy
100 Mbit/s, 2 ms, 0.1% loss-g8000,2,2the same (100 Mbit/s each)the same: reads are large, waits do not applythe same (73%)
-g6000,3,2
-g8000,2,3
20 Mbit/s, 5 ms, 0.5% loss-g8000,2,23% less41% less88% instead of 97%
-g6000,3,23% less35% less92%
-g8000,2,37% less46% less88%
5 Mbit/s, 25 ms, 1% loss-g8000,2,2the same (held by the loss)21-23% moreCPU-bound with or without
-g8000,2,105-8% less21-29% less
-g8000,2,2012-17% less31-32% less
+Grace delay pays where clients receive data steadily at a few tens of Mbit/s, +in small pieces, with DELAY matched to the speed: about the time TRAFF bytes +take to arrive. At 20 Mbit/s -g8000,2,2 takes about 40% off the CPU per +gigabyte for 3% of the throughput; at 5 Mbit/s 2 ms is too short and costs +CPU, while 10 ms takes a quarter off for 5-8%. On fast links it does nothing. +In these tests splice() and copying cost about the same once clients' links +are emulated; with grace delay on such streams splice() saves a little more. Many short exchanges gain +little either way. Nor does grace delay help once 3proxy's CPU is exhausted: +the waits then come on top of the queueing, and it degrades slightly, with a +little more CPU per exchange and longer delays. Choose DELAY so that the cap is above the speed +of a single client connection you want to serve in full, and TRAFF below the +read size you expect from a fast one. Grace delay does not help many small +requests and answers: they are too short to give NUM small reads in a row, and +the waits rarely apply.

logdump 1 1
is useful to see how grace +delays work.