git@vger.kernel.org mailing list mirror (one of many)
 help / color / mirror / code / Atom feed
From: Christian Couder <christian.couder@gmail.com>
To: Jeff Hostetler <git@jeffhostetler.com>
Cc: Derrick Stolee <stolee@gmail.com>, git <git@vger.kernel.org>,
	Junio C Hamano <gitster@pobox.com>,
	Jeff Hostetler <jeffhost@microsoft.com>,
	Jonathan Tan <jonathantanmy@google.com>,
	Christian Couder <chriscool@tuxfamily.org>
Subject: Re: [PATCH] sha1_file: remove static strbuf from sha1_file_name()
Date: Wed, 17 Jan 2018 18:55:22 +0100	[thread overview]
Message-ID: <CAP8UFD3bH+VqgPs6KNRc9UHLJiTVmHs5XX3fsh8wS+micGdJLQ@mail.gmail.com> (raw)
In-Reply-To: <b956f893-74c4-3b0e-1f0a-4be1b01dc649@jeffhostetler.com>

On Tue, Jan 16, 2018 at 8:00 PM, Jeff Hostetler <git@jeffhostetler.com> wrote:
>
>
> On 1/16/2018 9:01 AM, Derrick Stolee wrote:
>>
>> On 1/16/2018 2:18 AM, Christian Couder wrote:
>>>
>>> Using a static buffer in sha1_file_name() is error prone
>>> and the performance improvements it gives are not needed
>>> in most of the callers.
>>>
>>> So let's get rid of this static buffer and, if necessary
>>> or helpful, let's use one in the caller.
>>
>>
>> First: this is a good change for preventing bugs in the future. Do not let
>> my next thought deter you from making this change.
>>
>> Second: I wonder if there is any perf hit now that we are allocating
>> buffers much more often.

When I though that the caller might be performance sensitive, I used a
"static struct strbuf" in the caller to avoid any performance
regression.

Yeah, that means that further work is needed if we want to get rid of
all the static buffers, but that is not my goal. I am just concerned
with cleaning up sha1_file_name() before changing it, and avoiding
some possible trouble when using it.

Feel free to improve on that or even take over this series, otherwise
it can be part of the #leftoverbits.

>> Also, how often does get_object_directory() change,
>> so in some cases we could cache the buffer and only append the parts for the
>> loose object (and not reallocate because the filenames will have equal
>> length).

Again feel free to work on this kind of optimizations on top of this series.

>> I'm concerned about the perf implications when inspecting many loose
>> objects (100k+) but these code paths seem to be involved with more
>> substantial work, such as opening and parsing the objects, so keeping a
>> buffer in-memory is probably unnecessary.

Yeah, I also think it is not necessary to optimize too much.

>>> diff --git a/sha1_file.c b/sha1_file.c
>>> index 3da70ac650..f66c21b2da 100644
>>> --- a/sha1_file.c
>>> +++ b/sha1_file.c
>>> @@ -321,15 +321,11 @@ static void fill_sha1_path(struct strbuf *buf,
>>> const unsigned char *sha1)
>>>       }
>>>   }
>>> -const char *sha1_file_name(const unsigned char *sha1)
>>> +void sha1_file_name(struct strbuf *buf, const unsigned char *sha1)
>>>   {
>>> -    static struct strbuf buf = STRBUF_INIT;
>>> -
>>> -    strbuf_reset(&buf);
>>> -    strbuf_addf(&buf, "%s/", get_object_directory());
>>> +    strbuf_addf(buf, "%s/", get_object_directory());
>>> -    fill_sha1_path(&buf, sha1);
>>> -    return buf.buf;
>>> +    fill_sha1_path(buf, sha1);
>>>   }
>>
>>
>> Could you change this to use strbuf_addstr(buf, get_object_directory())
>> followed by a strbuf_addch(buf, '/')? This format string is unnecessary and
>> could become slow if this method is called in a tight loop.
>
> Yes, an _addstr() and _addch() would avoid a sprintf and
> we've seen perf problems with this before.
>
> Could we also add seed the initial buffer size to avoid
> any need to realloc as the buffer is filled in?
>
> Something like:
>         size_t len = strlen(get_object_directory()) + GIT_MAX_HEXSZ + 3;
>         strbuf_reset(&buf);
>         if (len > strbuf_avail(&buf))
>                 strbuf_grow(&buf, len);
>         strbuf_addstr(&buf, ...);

Ok, I did something like that in another patch on top of the first
patch which is just about using a "struct strbuf *" passed as an
argument instead of a static buffer.

>>>   struct strbuf *alt_scratch_buf(struct alternate_object_database *alt)
>>> @@ -710,7 +706,12 @@ int check_and_freshen_file(const char *fn, int
>>> freshen)
>>>   static int check_and_freshen_local(const unsigned char *sha1, int
>>> freshen)
>>>   {
>>> -    return check_and_freshen_file(sha1_file_name(sha1), freshen);
>>> +    static struct strbuf buf = STRBUF_INIT;
>>> +
>>> +    strbuf_reset(&buf);
>>> +    sha1_file_name(&buf, sha1);
>>> +
>>> +    return check_and_freshen_file(buf.buf, freshen);
>>>   }
>
> Does "buf" really need to be static here?  Doesn't this just move the
> problem from sha1_file_name() to here?

Yes, but maybe check_and_freshen_local() is performance sensitive, so
I think it is safer performance wise to still use a static buf.

If there is a consensus that it is ok to not use one here, I am ok to
change that. On the other hand the change could also be part of
another patch on top of this one...

>>>   static int check_and_freshen_nonlocal(const unsigned char *sha1, int
>>> freshen)
>>> @@ -866,8 +867,12 @@ static int stat_sha1_file(const unsigned char *sha1,
>>> struct stat *st,
>>>                 const char **path)
>>>   {
>>>       struct alternate_object_database *alt;
>>> +    static struct strbuf buf = STRBUF_INIT;
>>> +
>>> +    strbuf_reset(&buf);
>>> +    sha1_file_name(&buf, sha1);
>>> +    *path = buf.buf;
>>> -    *path = sha1_file_name(sha1);
>>>       if (!lstat(*path, st))
>>>           return 0;
>
> Again, making "buf" static here feels wrong.  Perhaps, change the signature
> of the static function to drop the const on the "path" and strbuf_detach
> buf.buf
> and give it to the caller (if path is not NULL) and otherwise release it
> here.

I don't think it is a good idea to do too many things in this patch,
so I think it should be part of another patch to change the way
stat_sha1_file() works.

>>> @@ -891,8 +896,12 @@ static int open_sha1_file(const unsigned char *sha1,
>>> const char **path)
>>>       int fd;
>>>       struct alternate_object_database *alt;
>>>       int most_interesting_errno;
>>> +    static struct strbuf buf = STRBUF_INIT;
>>> +
>>> +    strbuf_reset(&buf);
>>> +    sha1_file_name(&buf, sha1);
>>> +    *path = buf.buf;
>>> -    *path = sha1_file_name(sha1);
>>>       fd = git_open(*path);
>>>       if (fd >= 0)
>>>           return fd;
>
> Same comment here about the static strbuf.

Same comment for me too.

>> The check_and_freshen_nonlocal() and open_sha1_file() method comments

Actually I think it is stat_sha1_file() and open_sha1_file() that have
those comments.

>> warn
>> about the 'path' pointer and how it could be overwritten with another call
>> to sha1_file_name(). Your patch fixes that issue, so the comments can be
>> removed.
>
> Wait, we've eliminated the static buffer inside sha1_file_name(), but
> all of these callers currently define their own static buffers, so the
> comments still apply, right?

Yeah right, that's why I didn't change the comments.

> There is also an effectively static buffer in each of the alternates
> that is used by alt_sha1_path().

Yeah and I didn't change that, so anyway, if you want to get rid of
all the static buffers, other work than just this patch is needed.

>> Since these methods are currently being extra careful about the 'path'
>> parameter, perhaps these are candidates to keep a static buffer around?
>
> If I have multiple threads (as part of say a threaded checkout), we're
> still going to stumble over these static loose object pathname buffers,
> right?

I agree but again getting rid of all the static buffers is not the
goal of this patch series.

[...]

>> Thanks for this cleanup!
>
> Yes, thanks for digging into this!

Thanks for your comments,
Christian.

  reply	other threads:[~2018-01-17 17:55 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-01-16  7:18 [PATCH] sha1_file: remove static strbuf from sha1_file_name() Christian Couder
2018-01-16 14:01 ` Derrick Stolee
2018-01-16 19:00   ` Jeff Hostetler
2018-01-17 17:55     ` Christian Couder [this message]
2018-01-16 17:25 ` Kevin Daudt

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: http://vger.kernel.org/majordomo-info.html

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=CAP8UFD3bH+VqgPs6KNRc9UHLJiTVmHs5XX3fsh8wS+micGdJLQ@mail.gmail.com \
    --to=christian.couder@gmail.com \
    --cc=chriscool@tuxfamily.org \
    --cc=git@jeffhostetler.com \
    --cc=git@vger.kernel.org \
    --cc=gitster@pobox.com \
    --cc=jeffhost@microsoft.com \
    --cc=jonathantanmy@google.com \
    --cc=stolee@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://80x24.org/mirrors/git.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).