diff mbox series

[08/10] name-rev: pre-size buffer in get_parent_name()

Message ID c7f22d8d-10f8-0c97-672e-0a50182901e0@web.de (mailing list archive)
State New, archived
Headers show
Series name-rev: improve memory usage | expand

Commit Message

René Scharfe Feb. 4, 2020, 9:24 p.m. UTC
We can calculate the size of new name easily and precisely. Open-code
the xstrfmt() calls and grow the buffers as needed before filling them.
This provides a surprisingly large benefit when working with the
Chromium repository; here are the numbers measured using hyperfine
before:

Benchmark #1: ./git -C ../chromium/src name-rev --all
  Time (mean ± σ):      5.822 s ±  0.013 s    [User: 5.304 s, System: 0.516 s]
  Range (min … max):    5.803 s …  5.837 s    10 runs

... and with this patch:

Benchmark #1: ./git -C ../chromium/src name-rev --all
  Time (mean ± σ):      1.527 s ±  0.003 s    [User: 1.015 s, System: 0.511 s]
  Range (min … max):    1.524 s …  1.535 s    10 runs

Signed-off-by: René Scharfe <l.s.r@web.de>
---
 builtin/name-rev.c | 20 ++++++++++++++------
 1 file changed, 14 insertions(+), 6 deletions(-)

--
2.25.0

Comments

Derrick Stolee Feb. 5, 2020, 3:19 a.m. UTC | #1
On 2/4/2020 4:24 PM, René Scharfe wrote:
> We can calculate the size of new name easily and precisely. Open-code
> the xstrfmt() calls and grow the buffers as needed before filling them.
> This provides a surprisingly large benefit when working with the
> Chromium repository; here are the numbers measured using hyperfine
> before:
> 
> Benchmark #1: ./git -C ../chromium/src name-rev --all
>   Time (mean ± σ):      5.822 s ±  0.013 s    [User: 5.304 s, System: 0.516 s]
>   Range (min … max):    5.803 s …  5.837 s    10 runs
> 
> ... and with this patch:
> 
> Benchmark #1: ./git -C ../chromium/src name-rev --all
>   Time (mean ± σ):      1.527 s ±  0.003 s    [User: 1.015 s, System: 0.511 s]
>   Range (min … max):    1.524 s …  1.535 s    10 runs

Nice!

> +	if (name->generation > 0) {
> +		strbuf_grow(&sb, len +
> +			    1 + decimal_width(name->generation) +
> +			    1 + decimal_width(parent_number));

Just curious: these strbuf_grow() calls are what _really_ improve the
performance, right? If you dropped them, then can we expect the performance
to be similar to the old code?

> +		strbuf_addf(&sb, "%.*s~%d^%d", (int)len, name->tip_name,
> +			    name->generation, parent_number);
> +	} else {
> +		strbuf_grow(&sb, len +
> +			    1 + decimal_width(parent_number));
> +		strbuf_addf(&sb, "%.*s^%d", (int)len, name->tip_name,
> +			    parent_number);
> +	}
> +	return strbuf_detach(&sb, NULL);
>  }
> 
>  static void name_rev(struct commit *start_commit,
> --
> 2.25.0
>
René Scharfe Feb. 5, 2020, 3:16 p.m. UTC | #2
Am 05.02.20 um 04:19 schrieb Derrick Stolee:
> On 2/4/2020 4:24 PM, René Scharfe wrote:
>> We can calculate the size of new name easily and precisely. Open-code
>> the xstrfmt() calls and grow the buffers as needed before filling them.
>> This provides a surprisingly large benefit when working with the
>> Chromium repository; here are the numbers measured using hyperfine
>> before:
>>
>> Benchmark #1: ./git -C ../chromium/src name-rev --all
>>   Time (mean ± σ):      5.822 s ±  0.013 s    [User: 5.304 s, System: 0.516 s]
>>   Range (min … max):    5.803 s …  5.837 s    10 runs
>>
>> ... and with this patch:
>>
>> Benchmark #1: ./git -C ../chromium/src name-rev --all
>>   Time (mean ± σ):      1.527 s ±  0.003 s    [User: 1.015 s, System: 0.511 s]
>>   Range (min … max):    1.524 s …  1.535 s    10 runs
>
> Nice!
>
>> +	if (name->generation > 0) {
>> +		strbuf_grow(&sb, len +
>> +			    1 + decimal_width(name->generation) +
>> +			    1 + decimal_width(parent_number));
>
> Just curious: these strbuf_grow() calls are what _really_ improve the
> performance, right? If you dropped them, then can we expect the performance
> to be similar to the old code?

The replaced xstrfmt() is defined in strbuf.c and trivially wraps
xstrvfmt(), which in turn does:

	struct strbuf buf = STRBUF_INIT;
	strbuf_vaddf(&buf, fmt, ap);
	return strbuf_detach(&buf, NULL);

And strbuf_addf() trivially wraps strbuf_vaddf().  So indeed, all
I did was to inline xstrfmt() and add the strbuf_grow() calls.

Their high impact shocked me a bit.  vsnprintf(3) really doesn't seem
to like working with a buffer that is too small (when it's supposed to
just calculate how many bytes it would have needed), at least not in
the one from glibc 2.29.

>> +		strbuf_addf(&sb, "%.*s~%d^%d", (int)len, name->tip_name,
>> +			    name->generation, parent_number);
>> +	} else {
>> +		strbuf_grow(&sb, len +
>> +			    1 + decimal_width(parent_number));
>> +		strbuf_addf(&sb, "%.*s^%d", (int)len, name->tip_name,
>> +			    parent_number);
>> +	}
>> +	return strbuf_detach(&sb, NULL);
>>  }
>>
>>  static void name_rev(struct commit *start_commit,
>> --
>> 2.25.0
>>
>
diff mbox series

Patch

diff --git a/builtin/name-rev.c b/builtin/name-rev.c
index 6701fb1569..793356edd1 100644
--- a/builtin/name-rev.c
+++ b/builtin/name-rev.c
@@ -103,15 +103,23 @@  static struct rev_name *create_or_update_name(struct commit *commit,

 static char *get_parent_name(const struct rev_name *name, int parent_number)
 {
+	struct strbuf sb = STRBUF_INIT;
 	size_t len;

 	strip_suffix(name->tip_name, "^0", &len);
-	if (name->generation > 0)
-		return xstrfmt("%.*s~%d^%d", (int)len, name->tip_name,
-			       name->generation, parent_number);
-	else
-		return xstrfmt("%.*s^%d", (int)len, name->tip_name,
-			       parent_number);
+	if (name->generation > 0) {
+		strbuf_grow(&sb, len +
+			    1 + decimal_width(name->generation) +
+			    1 + decimal_width(parent_number));
+		strbuf_addf(&sb, "%.*s~%d^%d", (int)len, name->tip_name,
+			    name->generation, parent_number);
+	} else {
+		strbuf_grow(&sb, len +
+			    1 + decimal_width(parent_number));
+		strbuf_addf(&sb, "%.*s^%d", (int)len, name->tip_name,
+			    parent_number);
+	}
+	return strbuf_detach(&sb, NULL);
 }

 static void name_rev(struct commit *start_commit,