Skip to content

Commit 2a554dd

Browse files
committed
Merge branch 'ps/upload-pack-buffer-more-writes' into jch
Reduce system overhead "git upload-pack" spends relaying "git pack-objects" output to the "git fetch" running on the other end of the connection. * ps/upload-pack-buffer-more-writes: builtin/pack-objects: reduce lock contention when writing packfile data csum-file: drop `hashfd_throughput()` csum-file: introduce `hashfd_ext()` sideband: use writev(3p) to send pktlines wrapper: introduce writev(3p) wrappers compat/posix: introduce writev(3p) wrapper git-compat-util: introduce `cast_size_t_to_ssize_t()` upload-pack: reduce lock contention when writing packfile data upload-pack: adapt keepalives based on buffering upload-pack: fix debug statement when flushing packfile data
2 parents 6a6dc1b + b095803 commit 2a554dd

15 files changed

Lines changed: 217 additions & 41 deletions

Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2026,6 +2026,10 @@ ifdef NO_PREAD
20262026
COMPAT_CFLAGS += -DNO_PREAD
20272027
COMPAT_OBJS += compat/pread.o
20282028
endif
2029+
ifdef NO_WRITEV
2030+
COMPAT_CFLAGS += -DNO_WRITEV
2031+
COMPAT_OBJS += compat/writev.o
2032+
endif
20292033
ifdef NO_FAST_WORKING_DIRECTORY
20302034
BASIC_CFLAGS += -DNO_FAST_WORKING_DIRECTORY
20312035
endif

builtin/pack-objects.c

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
#include "promisor-remote.h"
4242
#include "pack-mtimes.h"
4343
#include "parse-options.h"
44+
#include "pkt-line.h"
4445
#include "blob.h"
4546
#include "tree.h"
4647
#include "path-walk.h"
@@ -1330,11 +1331,25 @@ static void write_pack_file(void)
13301331
unsigned char hash[GIT_MAX_RAWSZ];
13311332
char *pack_tmp_name = NULL;
13321333

1333-
if (pack_to_stdout)
1334-
f = hashfd_throughput(the_repository->hash_algo, 1,
1335-
"<stdout>", progress_state);
1336-
else
1334+
if (pack_to_stdout) {
1335+
/*
1336+
* This command is most often invoked via
1337+
* git-upload-pack(1), which will typically chunk data
1338+
* into pktlines. As such, we use the maximum data
1339+
* length of them as buffer length.
1340+
*
1341+
* Note that we need to subtract one though to
1342+
* accomodate for the sideband byte.
1343+
*/
1344+
struct hashfd_options opts = {
1345+
.progress = progress_state,
1346+
.buffer_len = LARGE_PACKET_DATA_MAX - 1,
1347+
};
1348+
f = hashfd_ext(the_repository->hash_algo, 1,
1349+
"<stdout>", &opts);
1350+
} else {
13371351
f = create_tmp_packfile(the_repository, &pack_tmp_name);
1352+
}
13381353

13391354
offset = write_pack_header(f, nr_remaining);
13401355

compat/posix.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,9 @@
137137
#include <sys/socket.h>
138138
#include <sys/ioctl.h>
139139
#include <sys/statvfs.h>
140+
#ifndef NO_WRITEV
141+
#include <sys/uio.h>
142+
#endif
140143
#include <termios.h>
141144
#ifndef NO_SYS_SELECT_H
142145
#include <sys/select.h>
@@ -323,6 +326,17 @@ int git_lstat(const char *, struct stat *);
323326
ssize_t git_pread(int fd, void *buf, size_t count, off_t offset);
324327
#endif
325328

329+
#ifdef NO_WRITEV
330+
#define writev git_writev
331+
#define iovec git_iovec
332+
struct git_iovec {
333+
void *iov_base;
334+
size_t iov_len;
335+
};
336+
337+
ssize_t git_writev(int fd, const struct iovec *iov, int iovcnt);
338+
#endif
339+
326340
#ifdef NO_SETENV
327341
#define setenv gitsetenv
328342
int gitsetenv(const char *, const char *, int);

compat/writev.c

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#include "../git-compat-util.h"
2+
#include "../wrapper.h"
3+
4+
ssize_t git_writev(int fd, const struct iovec *iov, int iovcnt)
5+
{
6+
size_t total_written = 0;
7+
8+
for (int i = 0; i < iovcnt; i++) {
9+
const char *bytes = iov[i].iov_base;
10+
size_t iovec_written = 0;
11+
12+
while (iovec_written < iov[i].iov_len) {
13+
ssize_t bytes_written = xwrite(fd, bytes + iovec_written,
14+
iov[i].iov_len - iovec_written);
15+
if (bytes_written < 0) {
16+
if (total_written)
17+
goto out;
18+
return bytes_written;
19+
}
20+
if (!bytes_written)
21+
goto out;
22+
iovec_written += bytes_written;
23+
total_written += bytes_written;
24+
}
25+
}
26+
27+
out:
28+
return cast_size_t_to_ssize_t(total_written);
29+
}

config.mak.uname

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,7 @@ ifeq ($(uname_S),Windows)
459459
SANE_TOOL_PATH ?= $(msvc_bin_dir_msys)
460460
HAVE_ALLOCA_H = YesPlease
461461
NO_PREAD = YesPlease
462+
NO_WRITEV = YesPlease
462463
NEEDS_CRYPTO_WITH_SSL = YesPlease
463464
NO_LIBGEN_H = YesPlease
464465
NO_POLL = YesPlease
@@ -674,6 +675,7 @@ ifeq ($(uname_S),MINGW)
674675
pathsep = ;
675676
HAVE_ALLOCA_H = YesPlease
676677
NO_PREAD = YesPlease
678+
NO_WRITEV = YesPlease
677679
NEEDS_CRYPTO_WITH_SSL = YesPlease
678680
NO_LIBGEN_H = YesPlease
679681
NO_POLL = YesPlease

csum-file.c

Lines changed: 8 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -161,26 +161,25 @@ struct hashfile *hashfd_check(const struct git_hash_algo *algop,
161161
return f;
162162
}
163163

164-
static struct hashfile *hashfd_internal(const struct git_hash_algo *algop,
165-
int fd, const char *name,
166-
struct progress *tp,
167-
size_t buffer_len)
164+
struct hashfile *hashfd_ext(const struct git_hash_algo *algop,
165+
int fd, const char *name,
166+
const struct hashfd_options *opts)
168167
{
169168
struct hashfile *f = xmalloc(sizeof(*f));
170169
f->fd = fd;
171170
f->check_fd = -1;
172171
f->offset = 0;
173172
f->total = 0;
174-
f->tp = tp;
173+
f->tp = opts->progress;
175174
f->name = name;
176175
f->do_crc = 0;
177176
f->skip_hash = 0;
178177

179178
f->algop = unsafe_hash_algo(algop);
180179
f->algop->init_fn(&f->ctx);
181180

182-
f->buffer_len = buffer_len;
183-
f->buffer = xmalloc(buffer_len);
181+
f->buffer_len = opts->buffer_len ? opts->buffer_len : 128 * 1024;
182+
f->buffer = xmalloc(f->buffer_len);
184183
f->check_buffer = NULL;
185184

186185
return f;
@@ -194,19 +193,8 @@ struct hashfile *hashfd(const struct git_hash_algo *algop,
194193
* measure the rate of data passing through this hashfile,
195194
* use a larger buffer size to reduce fsync() calls.
196195
*/
197-
return hashfd_internal(algop, fd, name, NULL, 128 * 1024);
198-
}
199-
200-
struct hashfile *hashfd_throughput(const struct git_hash_algo *algop,
201-
int fd, const char *name, struct progress *tp)
202-
{
203-
/*
204-
* Since we are expecting to report progress of the
205-
* write into this hashfile, use a smaller buffer
206-
* size so the progress indicators arrive at a more
207-
* frequent rate.
208-
*/
209-
return hashfd_internal(algop, fd, name, tp, 8 * 1024);
196+
struct hashfd_options opts = { 0 };
197+
return hashfd_ext(algop, fd, name, &opts);
210198
}
211199

212200
void hashfile_checkpoint_init(struct hashfile *f,

csum-file.h

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,24 @@ int hashfile_truncate(struct hashfile *, struct hashfile_checkpoint *);
4545
#define CSUM_FSYNC 2
4646
#define CSUM_HASH_IN_STREAM 4
4747

48+
struct hashfd_options {
49+
/*
50+
* Throughput progress that counts the number of bytes that have been
51+
* hashed.
52+
*/
53+
struct progress *progress;
54+
55+
/* The length of the buffer that shall be used read read data. */
56+
size_t buffer_len;
57+
};
58+
59+
struct hashfile *hashfd_ext(const struct git_hash_algo *algop,
60+
int fd, const char *name,
61+
const struct hashfd_options *opts);
4862
struct hashfile *hashfd(const struct git_hash_algo *algop,
4963
int fd, const char *name);
5064
struct hashfile *hashfd_check(const struct git_hash_algo *algop,
5165
const char *name);
52-
struct hashfile *hashfd_throughput(const struct git_hash_algo *algop,
53-
int fd, const char *name, struct progress *tp);
5466

5567
/*
5668
* Free the hashfile without flushing its contents to disk. This only

git-compat-util.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -665,6 +665,14 @@ static inline int cast_size_t_to_int(size_t a)
665665
return (int)a;
666666
}
667667

668+
static inline ssize_t cast_size_t_to_ssize_t(size_t a)
669+
{
670+
if (a > maximum_signed_value_of_type(ssize_t))
671+
die("number too large to represent as ssize_t on this platform: %"PRIuMAX,
672+
(uintmax_t)a);
673+
return (ssize_t)a;
674+
}
675+
668676
static inline uint64_t u64_mult(uint64_t a, uint64_t b)
669677
{
670678
if (unsigned_mult_overflows(a, b))

meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1416,6 +1416,7 @@ checkfuncs = {
14161416
'initgroups' : [],
14171417
'strtoumax' : ['strtoumax.c', 'strtoimax.c'],
14181418
'pread' : ['pread.c'],
1419+
'writev' : ['writev.c'],
14191420
}
14201421

14211422
if host_machine.system() == 'windows'

sideband.c

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,7 @@ void send_sideband(int fd, int band, const char *data, ssize_t sz, int packet_ma
443443
const char *p = data;
444444

445445
while (sz) {
446+
struct iovec iov[2];
446447
unsigned n;
447448
char hdr[5];
448449

@@ -452,12 +453,19 @@ void send_sideband(int fd, int band, const char *data, ssize_t sz, int packet_ma
452453
if (0 <= band) {
453454
xsnprintf(hdr, sizeof(hdr), "%04x", n + 5);
454455
hdr[4] = band;
455-
write_or_die(fd, hdr, 5);
456+
iov[0].iov_base = hdr;
457+
iov[0].iov_len = 5;
456458
} else {
457459
xsnprintf(hdr, sizeof(hdr), "%04x", n + 4);
458-
write_or_die(fd, hdr, 4);
460+
iov[0].iov_base = hdr;
461+
iov[0].iov_len = 4;
459462
}
460-
write_or_die(fd, p, n);
463+
464+
iov[1].iov_base = (void *) p;
465+
iov[1].iov_len = n;
466+
467+
writev_or_die(fd, iov, ARRAY_SIZE(iov));
468+
461469
p += n;
462470
sz -= n;
463471
}

0 commit comments

Comments
 (0)