Skip to content

Commit 0cc30e0

Browse files
peffgitster
authored andcommitted
strbuf_getwholeline: use getdelim if it is available
We spend a lot of time in strbuf_getwholeline in a tight loop reading characters from a stdio handle into a buffer. The libc getdelim() function can do this for us with less overhead. It's in POSIX.1-2008, and was a GNU extension before that. Therefore we can't rely on it, but can fall back to the existing getc loop when it is not available. The HAVE_GETDELIM knob is turned on automatically for Linux, where we have glibc. We don't need to set any new feature-test macros, because we already define _GNU_SOURCE. Other systems that implement getdelim may need to other macros (probably _POSIX_C_SOURCE >= 200809L), but we can address that along with setting the Makefile knob after testing the feature on those systems. Running "git rev-parse refs/heads/does-not-exist" on a repo with an extremely large (1.6GB) packed-refs file went from (best-of-5): real 0m8.601s user 0m8.084s sys 0m0.524s to: real 0m6.768s user 0m6.340s sys 0m0.432s for a wall-clock speedup of 21%. Based on a patch from Rasmus Villemoes <[email protected]>. Signed-off-by: Jeff King <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent f80c153 commit 0cc30e0

File tree

3 files changed

+49
-0
lines changed

3 files changed

+49
-0
lines changed

Makefile

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,8 @@ all::
359359
# compiler is detected to support it.
360360
#
361361
# Define HAVE_BSD_SYSCTL if your platform has a BSD-compatible sysctl function.
362+
#
363+
# Define HAVE_GETDELIM if your system has the getdelim() function.
362364

363365
GIT-VERSION-FILE: FORCE
364366
@$(SHELL_PATH) ./GIT-VERSION-GEN
@@ -1437,6 +1439,10 @@ ifdef HAVE_BSD_SYSCTL
14371439
BASIC_CFLAGS += -DHAVE_BSD_SYSCTL
14381440
endif
14391441

1442+
ifdef HAVE_GETDELIM
1443+
BASIC_CFLAGS += -DHAVE_GETDELIM
1444+
endif
1445+
14401446
ifeq ($(TCLTK_PATH),)
14411447
NO_TCLTK = NoThanks
14421448
endif

config.mak.uname

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ ifeq ($(uname_S),Linux)
3636
HAVE_DEV_TTY = YesPlease
3737
HAVE_CLOCK_GETTIME = YesPlease
3838
HAVE_CLOCK_MONOTONIC = YesPlease
39+
HAVE_GETDELIM = YesPlease
3940
endif
4041
ifeq ($(uname_S),GNU/kFreeBSD)
4142
HAVE_ALLOCA_H = YesPlease

strbuf.c

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,47 @@ int strbuf_getcwd(struct strbuf *sb)
435435
return -1;
436436
}
437437

438+
#ifdef HAVE_GETDELIM
439+
int strbuf_getwholeline(struct strbuf *sb, FILE *fp, int term)
440+
{
441+
ssize_t r;
442+
443+
if (feof(fp))
444+
return EOF;
445+
446+
strbuf_reset(sb);
447+
448+
/* Translate slopbuf to NULL, as we cannot call realloc on it */
449+
if (!sb->alloc)
450+
sb->buf = NULL;
451+
r = getdelim(&sb->buf, &sb->alloc, term, fp);
452+
453+
if (r > 0) {
454+
sb->len = r;
455+
return 0;
456+
}
457+
assert(r == -1);
458+
459+
/*
460+
* Normally we would have called xrealloc, which will try to free
461+
* memory and recover. But we have no way to tell getdelim() to do so.
462+
* Worse, we cannot try to recover ENOMEM ourselves, because we have
463+
* no idea how many bytes were read by getdelim.
464+
*
465+
* Dying here is reasonable. It mirrors what xrealloc would do on
466+
* catastrophic memory failure. We skip the opportunity to free pack
467+
* memory and retry, but that's unlikely to help for a malloc small
468+
* enough to hold a single line of input, anyway.
469+
*/
470+
if (errno == ENOMEM)
471+
die("Out of memory, getdelim failed");
472+
473+
/* Restore slopbuf that we moved out of the way before */
474+
if (!sb->buf)
475+
strbuf_init(sb, 0);
476+
return EOF;
477+
}
478+
#else
438479
int strbuf_getwholeline(struct strbuf *sb, FILE *fp, int term)
439480
{
440481
int ch;
@@ -458,6 +499,7 @@ int strbuf_getwholeline(struct strbuf *sb, FILE *fp, int term)
458499
sb->buf[sb->len] = '\0';
459500
return 0;
460501
}
502+
#endif
461503

462504
int strbuf_getline(struct strbuf *sb, FILE *fp, int term)
463505
{

0 commit comments

Comments
 (0)