Skip to content

git_submodule_lookup is O(N) w.r.t. number of submodules #3756

Closed

Description

@bpeabody

causing many operations, such as git_rebase_init (which results in calls to git_submodule_lookup several times for each submodule) to be O(N^2).

On a 3.1Ghz MBP with SSD, for example, a single call to git_submodule_lookup in a repository with 4,000 submodules takes over 20ms.

I am very happy to work on this myself and submit a patch, but I wanted to open a discussion and get advice on how to proceed. One suggestion: allow an option to configure a repository to cache this information on first submodule access.

Activity

  1. bpeabody commented on Apr 26, 2016

    @bpeabody
    Author

    Also, git_submodule_status is very slow as it updates the submodules head status every time; this method is called several times for each submodule during, e.g. a git_rebase_init.

  2. bpeabody commented on Apr 26, 2016

    @bpeabody
    Author

    Also, the all_submodules function in submodules.c is itself O(N^2) as it searches the .gitmodules entries, linearly, for each submodule.

  3. bpeabody commented on Apr 26, 2016

    @bpeabody
    Author

    I have a preliminary patch I am working on (pending permission from my employer) that adds:

    • git_submodule_cache_all and git_submodule_clear_cache methods that when called cause git_submodule_lookup to return values from a cache that loads once in O(N) time.
    • When the cache is present, git_submodule_status skips updating submodule head/index state.
    • Optimized all_submodules so that a single map of names is loaded in O(N) time from the .gitmodules file.
  4. bpeabody commented on Apr 26, 2016

    @bpeabody
    Author

    The changes make git_rebase_init take around 250ms on my laptop, down from about 3 minutes, for a repo with 4,000 submodules. I can upload this sample repo if anyone wants to play with it.

  5. carlosmn commented on Apr 27, 2016

    @carlosmn
    Member

    The submodule code does assume you're going to be using just a few. I'm not sure about adding a brand new cache, but one thing that we can do to avoid parsing the file multiple times is keep a git_config in the repository to represent the .gitmodules file and take a snapshot of that in open_gitmodules() (or initialize that the first time). The read part of the config code can already avoid parsing a file if its contents haven't changed. It would read in the contents of the file to determine that it hasn't changed, but it would keep the already-parsed version in memory when creating the snapshots.

  6. bpeabody commented on Apr 27, 2016

    @bpeabody
    Author

    I can see how the brand new submodule cache might not be the most desirable fix. The solution you're proposing with .gitmodules caching sounds better, but it's still going to leave most operations too slow for me.

    Beyond the O(N^2) issue with the .gitmodules file, bothgit_submodule_status and (I think) git_submodule_lookup are bottlenecked on git_repository_head_tree, which takes close to a millisecond; when it's called several times per submodule during git_rebase_init, it can be a big performance hit.

    I think, more philosophically, I am also generally assuming that you will only have a few submodules open at any given time. If you have 4,000 submodules checked out locally, I think it's reasonable for git_rebase to be a little slow (though I would still expect the time to scale linearly). Maybe a better solution (in addition to the caching of the data in .gitmodules) would be to optimize the behavior some functions to be more efficient in dealing with submodules that aren't checked out, such as git_submodule_status, git_submodule_lookup.

  7. bpeabody commented on Apr 27, 2016

    @bpeabody
    Author

    many-submodules.tar.gz

    If you want to play around with a repo having lots of submodules, I've uploaded one here with about 4,000. You can see that just the git_rebase_init of master onto foo takes minutes right now, even though no submodules are actually checked out and the rebase is a fast-forward.

  8. ethomson commented on May 26, 2016

    @ethomson
    Member

    @bpeabody You mentioned that you were waiting on permission from your employer, is this something that they granted? Is this a project you are still tackling?

  9. bpeabody commented on May 26, 2016

    @bpeabody
    Author

    Yes, I have permission to contribute, and even better, the project I'm using this for has been approved and officially open-sourced: https://github.com/twosigma/git-meta.

    I hope to get back to this issue next week and contribute an initial patch along the lines of the suggestion from @carlosmn; I don't think that will be entirely sufficient but it will be a good start and we can work from there.

  10. bpeabody commented on May 26, 2016

    @bpeabody
    Author

    By "not entirely sufficient", I mean that the suggestion will address the O(N) issue, but that there would still remain prohibitively high constant factors associated with opening submodules.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions