Relays: the size of a splice() pipe chosen at build time

sockmap() and the callbacks' relay each had their own 64K for what a
pipe is given at most (MAXSPLICE, CBSPLICE). It is PIPESIZE now, one
for both, 64K as before: what a pipe holds unless asked otherwise.
Built with -DPIPESIZE=<bytes>, pipes are made that large with
F_SETPIPE_SZ (pipenew()); one the system will not make so large is not
used, and the data is copied, as when there is no pipe to be had.

Measured on ARM and Intel (mix-http, mix-relay), 16K and 32K were
slower, 128K and 256K no faster than 64K, which stays the default.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Vladimir Dubrovin 2026-10-28 11:49:38 +03:00
parent 17a5287fce
commit 143158e97f
3 changed files with 37 additions and 16 deletions

View File

@ -446,8 +446,6 @@ DONE:
#define SPLICE_F_NONBLOCK 0x02
#endif
ssize_t splice(int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, size_t len, unsigned int flags);
/* what a pipe of splice() takes at once, as sockmap()'s MAXSPLICE */
#define CBSPLICE 65536
#endif
/* One way of the relay: from the side src to dst, through the buffer of
@ -621,13 +619,13 @@ static int cbway(struct cbmap *m, struct cbway *w){
/* what was buffered goes first: the pipe is read once it is
through */
if(!inb && !w->wantbuf && w->left > w->inpipe && !m->term_r[w->src] && !end && w->inpipe < CBSPLICE &&
if(!inb && !w->wantbuf && w->left > w->inpipe && !m->term_r[w->src] && !end && w->inpipe < PIPESIZE &&
(rs->ready & (POLLIN | POLLHUP | POLLERR))){
if(pipe[0] < 0 && pipe2(pipe, O_NONBLOCK) < 0){
if(pipe[0] < 0 && pipenew(pipe) < 0){
w->splice = 0;
return 1;
}
n = splice(from, NULL, pipe[1], NULL, (size_t)CBMIN((uint64_t)CBSPLICE - w->inpipe,
n = splice(from, NULL, pipe[1], NULL, (size_t)CBMIN((uint64_t)PIPESIZE - w->inpipe,
w->left - w->inpipe), SPLICE_F_NONBLOCK | SPLICE_F_MOVE);
if(n > 0){
w->inpipe += n;

View File

@ -628,6 +628,12 @@ extern struct commands commandhandlers[];
#define SPLICE_TLS 1
#define SPLICE_TLSPENDING 2
extern int (*splicetls)(struct clientparam *param, SOCKET s);
/* what a pipe of splice() is given at most: 64K, what a pipe holds;
built with -DPIPESIZE=<bytes>, pipes are made that large */
#ifndef PIPESIZE
#define PIPESIZE 65536
#endif
int pipenew(int p[2]);
#define mapsocket(a,b) ((a->srv->usesplice && !a->nosplice && !a->ndatfilterssrv && !a->ndatfilterscli)?sockmap(a,b,a->srv->usesplice):sockmap(a,b,0))
#else
#define mapsocket(a,b) sockmap(a,b, 0)

View File

@ -36,12 +36,29 @@ ssize_t splice(int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, size_t le
#define SPLICE_F_GIFT 0x08
#endif
#ifndef F_SETPIPE_SZ
#define F_SETPIPE_SZ 1031
#endif
#define MAXSPLICE 65536
/* splice() refuses a kernel TLS record that is not data: EINVAL, or EIO
and EBADMSG on some kernels */
#define SPLICEREFUSED(e) ((e) == EINVAL || (e) == EIO || (e) == EBADMSG)
/* A pipe for splice(), nonblocking, of PIPESIZE: -1 if there is none to be
had, or not of that size. */
int pipenew(int p[2]){
if(pipe2(p, O_NONBLOCK) < 0) return -1;
#if PIPESIZE != 65536
if(fcntl(p[0], F_SETPIPE_SZ, PIPESIZE) < 0){
close(p[0]);
close(p[1]);
p[0] = p[1] = -1;
return -1;
}
#endif
return 0;
}
/* With SPLICE_AUTO, when a direction allowed to splice actually does
(without it, an allowed direction always does).
A body of known length is spliced if at least splicemin of it is left
@ -391,7 +408,7 @@ log("done send to client from buf");
#ifdef WITHLOG
log("send to server from pipe");
#endif
res = splice(pipecli[0], NULL, param->remsock, NULL, MIN(MAXSPLICE, inclientpipe), SPLICE_F_NONBLOCK|SPLICE_F_MOVE);
res = splice(pipecli[0], NULL, param->remsock, NULL, MIN(PIPESIZE, inclientpipe), SPLICE_F_NONBLOCK|SPLICE_F_MOVE);
/* from a pipe holding data: the socket is full */
if(res < 0 && errno == EAGAIN && fiberblockfunc) fiberblockfunc(param->remsock, POLLOUT);
#ifdef WITHLOG
@ -430,7 +447,7 @@ log(logbuf);
#ifdef WITHLOG
log("send to client from pipe");
#endif
res = splice(pipesrv[0], NULL, param->clisock, NULL, MIN(MAXSPLICE, inserverpipe), SPLICE_F_NONBLOCK|SPLICE_F_MOVE);
res = splice(pipesrv[0], NULL, param->clisock, NULL, MIN(PIPESIZE, inserverpipe), SPLICE_F_NONBLOCK|SPLICE_F_MOVE);
/* from a pipe holding data: the socket is full */
if(res < 0 && errno == EAGAIN && fiberblockfunc) fiberblockfunc(param->clisock, POLLOUT);
#ifdef WITHLOG
@ -462,7 +479,7 @@ log(logbuf);
#ifdef WITHLOG
log("read from client to pipe");
#endif
if(pipecli[0] < 0 && pipe2(pipecli, O_NONBLOCK) < 0){
if(pipecli[0] < 0 && pipenew(pipecli) < 0){
/* no pipe to be had: copy instead */
allowcli = splicecli = TOCLIENTPIPE = FROMCLIENTPIPE = 0;
if(param->cliinbuf < CLIROOM) TOCLIENTBUF = 1;
@ -479,7 +496,7 @@ log("read from client to pipe");
continue;
}
errno = 0;
res = splice(param->clisock, NULL, pipecli[1], NULL, (int)MIN((uint64_t)MAXSPLICE - inclientpipe, (uint64_t)fromclient-inclientpipe), SPLICE_F_NONBLOCK|SPLICE_F_MOVE);
res = splice(param->clisock, NULL, pipecli[1], NULL, (int)MIN((uint64_t)PIPESIZE - inclientpipe, (uint64_t)fromclient-inclientpipe), SPLICE_F_NONBLOCK|SPLICE_F_MOVE);
#ifdef WITHLOG
log("client to pipe splice finished\n");
#if WITHLOG > 1
@ -521,7 +538,7 @@ log("done read from client to pipe");
else if(++streakcli > splicestreak) wantbufcli = 1;
}
inclientpipe += res;
if(inclientpipe >= MAXSPLICE) TOCLIENTPIPE = 0;
if(inclientpipe >= PIPESIZE) TOCLIENTPIPE = 0;
needaction = 0;
continue;
}
@ -530,7 +547,7 @@ log("done read from client to pipe");
#ifdef WITHLOG
log("read from server to pipe\n");
#endif
if(pipesrv[0] < 0 && pipe2(pipesrv, O_NONBLOCK) < 0){
if(pipesrv[0] < 0 && pipenew(pipesrv) < 0){
allowsrv = splicesrv = TOSERVERPIPE = FROMSERVERPIPE = 0;
if(param->srvinbuf < SRVROOM) TOSERVERBUF = 1;
continue;
@ -544,7 +561,7 @@ log("read from server to pipe\n");
continue;
}
errno = 0;
res = splice(param->remsock, NULL, pipesrv[1], NULL, MIN(MAXSPLICE - inserverpipe, fromserver - inserverpipe), SPLICE_F_NONBLOCK|SPLICE_F_MOVE);
res = splice(param->remsock, NULL, pipesrv[1], NULL, MIN(PIPESIZE - inserverpipe, fromserver - inserverpipe), SPLICE_F_NONBLOCK|SPLICE_F_MOVE);
#ifdef WITHLOG
log("server to pipe splice finished\n");
#if WITHLOG > 1
@ -586,7 +603,7 @@ log("done read from server to pipe\n");
param->nreads++;
param->statssrv64 += res;
inserverpipe += res;
if(inserverpipe >= MAXSPLICE) TOSERVERPIPE = 0;
if(inserverpipe >= PIPESIZE) TOSERVERPIPE = 0;
if(param->bandlimfunc) {
int sl1;
sl1 = (*param->bandlimfunc)(param, res, 0);
@ -876,7 +893,7 @@ log("server terminated connection");
// }
#ifdef WITHSPLICE
{
if(splicecli && fromclient>inclientpipe && !TOCLIENTPIPE && inclientpipe < MAXSPLICE){
if(splicecli && fromclient>inclientpipe && !TOCLIENTPIPE && inclientpipe < PIPESIZE){
if(!after){
#ifdef WITHLOG
log("wait writing to client pipe");
@ -916,7 +933,7 @@ log("ready reading from client pipe");
}
fdsc++;
}
if(splicesrv && fromserver>inserverpipe && !TOSERVERPIPE && inserverpipe < MAXSPLICE){
if(splicesrv && fromserver>inserverpipe && !TOSERVERPIPE && inserverpipe < PIPESIZE){
if(!after){
#ifdef WITHLOG
log("wait writing to server pipe");