Hi Peff,
On Thu, 27 Jun 2019, Jeff King wrote:
> On Thu, Jun 27, 2019 at 01:25:15AM -0400, Jeff King wrote:
>
> > Taylor and I noticed a slowdown in p1451 between v2.20.1 and v2.21.0. I
> > was surprised to find that it bisects to bbb15c5193 (fsck: reduce word
> > legos to help i18n, 2018-11-10).
> >
> > The important part, as it turns out, is the switch to using fprintf_ln()
> > instead of a regular fprintf() with a "\n" in it. Doing this:
> > [...]
> > on top of the current tip of master yields this result:
> >
> > Test HEAD^ HEAD
> >
> > -----------------------------------------------------------------------------------------
> > 1451.3: fsck with 0 skipped bad commits 9.78(7.46+2.32)
> > 8.74(7.38+1.36) -10.6%
> > 1451.5: fsck with 1 skipped bad commits 9.78(7.66+2.11)
> > 8.49(7.04+1.44) -13.2%
> > 1451.7: fsck with 10 skipped bad commits 9.83(7.45+2.37)
> > 8.53(7.26+1.24) -13.2%
> > 1451.9: fsck with 100 skipped bad commits 9.87(7.47+2.40)
> > 8.54(7.24+1.30) -13.5%
> > 1451.11: fsck with 1000 skipped bad commits 9.79(7.67+2.12)
> > 8.48(7.25+1.23) -13.4%
> > 1451.13: fsck with 10000 skipped bad commits 9.86(7.58+2.26)
> > 8.38(7.09+1.28) -15.0%
> > 1451.15: fsck with 100000 skipped bad commits 9.58(7.39+2.19)
> > 8.41(7.21+1.19) -12.2%
> > 1451.17: fsck with 1000000 skipped bad commits 6.38(6.31+0.07)
> > 6.35(6.26+0.07) -0.5%
>
> Ah, I think I see it.
>
> See how the system times for HEAD^ (with fprintf_ln) are higher? We're
> flushing stderr more frequently (twice as much, since it's unbuffered,
> and we now have an fprintf followed by a putc).
>
> I can get similar speedups by formatting into a buffer:
>
> diff --git a/strbuf.c b/strbuf.c
> index 0e18b259ce..07ce9b9178 100644
> --- a/strbuf.c
> +++ b/strbuf.c
> @@ -880,8 +880,22 @@ int printf_ln(const char *fmt, ...)
>
> int fprintf_ln(FILE *fp, const char *fmt, ...)
> {
> + char buf[1024];
> int ret;
> va_list ap;
> +
> + /* Fast path: format it ourselves and dump it via fwrite. */
> + va_start(ap, fmt);
> + ret = vsnprintf(buf, sizeof(buf), fmt, ap);
> + va_end(ap);
> + if (ret < sizeof(buf)) {
> + buf[ret++] = '\n';
> + if (fwrite(buf, 1, ret, fp) != ret)
> + return -1;
> + return ret;
> + }
> +
> + /* Slow path: a normal fprintf/putc combo */
> va_start(ap, fmt);
> ret = vfprintf(fp, fmt, ap);
> va_end(ap);
>
> But we shouldn't have to resort to that.
Why not?
It would make for a perfectly fine excuse to finally get work going in
direction of a initially heap-backed strbuf. Which we have wanted for ages
now.
> We can use setvbuf() to toggle buffering back and forth, but I'm not
> sure if there's a way to query the current buffering scheme for a stdio
> stream.
It also is not very safe, especially when we want to have this work in a
multi-threaded fashion.
I'd be much more comfortable with rendering the string into a buffer and
then sending that buffer wholesale to stderr.
Ciao,
Dscho