git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [PATCH v4 6/7] revision.c: generation-based topo-order algorithm

From
Jakub Narebski <jnareb@gmail.com>
Date
Oct 26, 2018, 16:55 UTC
Message-ID
<86bm7g641x.fsf@gmail.com>
In-Reply-To
<6501930f-4097-1b81-6d0d-be54f050a5a4@gmail.com>
Derrick Stolee <stolee@gmail.com> writes:
> On 10/22/2018 9:37 AM, Jakub Narebski wrote:
>> "Derrick Stolee via GitGitGadget" <gitgitgadget@gmail.com> writes:
[...]
Show 5 quoted lines
>> Sidenote: the new algorithm looks a bit like Unix pipeline, where each
>> step of pipeline does not output much more than next step needs /
>> requests.
>
> That's essentially the idea.

Some of the newer languages have built-in support for similar kind of pipeline for connecting processes, be it channels in Go, supplies and suppliers in Perl6. I wonder if there exists some library implementing this kind of construct in C.

That aside, I wonder if when there would be support for more reachability indices than generation numbers, if it wouldn't be better to pass a commit as a limiter (up to this commit), than specific indices like current passing of generation number. Just food for thoughs...

[...]
Show 46 quoted lines
>>> In my local testing, I used the following Git commands on the
>>> Linux repository in three modes: HEAD~1 with no commit-graph,
>>> HEAD~1 with a commit-graph, and HEAD with a commit-graph. This
>>> allows comparing the benefits we get from parsing commits from
>>> the commit-graph and then again the benefits we get by
>>> restricting the set of commits we walk.
>>>
>>> Test: git rev-list --topo-order -100 HEAD
>>> HEAD~1, no commit-graph: 6.80 s
>>> HEAD~1, w/ commit-graph: 0.77 s
>>>    HEAD, w/ commit-graph: 0.02 s
>>>
>>> Test: git rev-list --topo-order -100 HEAD -- tools
>>> HEAD~1, no commit-graph: 9.63 s
>>> HEAD~1, w/ commit-graph: 6.06 s
>>>    HEAD, w/ commit-graph: 0.06 s
>>>
>>> This speedup is due to a few things. First, the new generation-
>>> number-enabled algorithm walks commits on order of the number of
>>> results output (subject to some branching structure expectations).
>>> Since we limit to 100 results, we are running a query similar to
>>> filling a single page of results. Second, when specifying a path,
>>> we must parse the root tree object for each commit we walk. The
>>> previous benefits from the commit-graph are entirely from reading
>>> the commit-graph instead of parsing commits. Since we need to
>>> parse trees for the same number of commits as before, we slow
>>> down significantly from the non-path-based query.
>>>
>>> For the test above, I specifically selected a path that is changed
>>> frequently, including by merge commits. A less-frequently-changed
>>> path (such as 'README') has similar end-to-end time since we need
>>> to walk the same number of commits (before determining we do not
>>> have 100 hits). However, get the benefit that the output is
>>> presented to the user as it is discovered, much the same as a
>>> normal 'git log' command (no '--topo-order'). This is an improved
>>> user experience, even if the command has the same runtime.
>>>
>> First, do I understand it correctly that in first case the gains from
>> new algorithms are so slim because with commit-graph file and no path
>> limiting we don't hit repository anyway; we walk less commits, but
>> reading commit data from commit-graph file is fast/
>
> If you mean 0.77s to 0.02s is "slim" then yes, it is because the
> commit-graph command already made a full walk of the commit history
> faster. (I'm only poking at this because the _relative_ improvement is
> significant, even if the command was already sub-second.)

First, you didn't provide us with percentages, i.e. relative improvement (and I am lazy). Second, 0.02s can be within the margin of error, depending on how it is measured, and how stable this measurement is.

Show 7 quoted lines
>> Second, I wonder if there is some easy way to perform automatic latency
>> tests, i.e. how fast does Git show the first page of output...
>
> I have talked with Jeff Hostetler about this, to see if we can have a
> "time to first page" traced with trace2, but we don't seem to have
> access to that information within Git. We would need to insert it into
> the pager. The "-100" is used instead.

Perhaps another solution to the problem of "first page of output" latency tests could be feasible, namely create a helper test-pager-1p "pager" program that would automatically quit after first page of output; or perhaps even one that benchmarks each page of output automatically.

There exists 'pv' (pipe viewer) program for pipes, so I think it would be possible to do equivalent, but as a pager.

[...]
Show 20 quoted lines
>>> +static inline void test_flag_and_insert(struct prio_queue *q, struct commit *c, int flag)
>>> +{
>>> +	if (c->object.flags & flag)
>>> +		return;
>>> +
>>> +	c->object.flags |= flag;
>>> +	prio_queue_put(q, c);
>>> +}
>>
>> This is an independent change, though I see that it is quite specific
>> (as opposed to quite generic prio_queue_peek() operation added earlier
>> in this series), so it does not make much sense as standalone change.
>>
>> It inserts commit into priority queue only if it didn't have flags set,
>> and sets the flag (so we won't add it to the queue again, not without
>> unsetting the flag), am I correct?
>
> Yes, this pattern of using a flag to avoid duplicate entries in the
> priority queue appears in multiple walks. It wasn't needed before. We
> call it four times in the code below.
Show 8 quoted lines
>> I guess that we use test_flag_and_insert() instead of prio_queue_put()
>> to avoid duplicate entries in the queue.  I think the queue is initially
>> populated with the starting commits, but those need not to be
>> unreachable from each other, and walking down parents we can encounter
>> starting commit already in the queue.  Am I correct?
>
> We can also reach commits in multiple ways, so the initial conditions
> are not the only ways to insert duplicates.
Right.
[...]
Show 11 quoted lines
>>> +	if (c->object.flags & UNINTERESTING)
>>> +		mark_parents_uninteresting(c);
>>> +
>>> +	for (p = c->parents; p; p = p->next)
>>> +		test_flag_and_insert(&info->explore_queue, p->item, TOPO_WALK_EXPLORED);
>>
>> Do we need to insert parents to the queue even if they were marked
>> UNINTERESTING?
>
> We need to propagate the UNINTERESTING flag to our parents. That
> propagation happens in process_parents().

I think I understand. We need to propagate UNINTERESTING flag down the chain, isn't it?

[...]
Show 16 quoted lines
>>>     static void init_topo_walk(struct rev_info *revs)
>>>   {
>>>   	struct topo_walk_info *info;
>>> +	struct commit_list *list;
>>>   	revs->topo_walk_info = xmalloc(sizeof(struct topo_walk_info));
>>>   	info = revs->topo_walk_info;
>>>   	memset(info, 0, sizeof(struct topo_walk_info));
>>>   -	limit_list(revs);
>>> -	sort_in_topological_order(&revs->commits, revs->sort_order);
>>> +	init_indegree_slab(&info->indegree);
>>> +	memset(&info->explore_queue, '\0', sizeof(info->explore_queue));
>>> +	memset(&info->indegree_queue, '\0', sizeof(info->indegree_queue));
>>> +	memset(&info->topo_queue, '\0', sizeof(info->topo_queue));
>>
>> Why this memset uses '\0' as a filler value and not 0?  The queues are
>> not strings [and you use 0 in other places].
I think you missed answering about this issue.
[...]
>> Looks all right.
>
> Thanks for taking the time on this huge patch!
You are welcome.
-- 
Jakub Narębski
Previous: Derrick StoleeNext: Derrick Stolee via GitGitGadget
Message 68 of 87 in “Use generation numbers for --topo-order”
  1. 0/6 Use generation numbers for --topo-orderDerrick Stolee via GitGitGadget, Aug 27, 2018
  2. 1/6 prio-queue: add 'peek' operationDerrick Stolee via GitGitGadget, Aug 27, 2018
  3. 2/6 test-reach: add run_three_modes methodDerrick Stolee via GitGitGadget, Aug 27, 2018
  4. 3/6 test-reach: add rev-list testsDerrick Stolee via GitGitGadget, Aug 27, 2018
  5. 4/6 revision.c: begin refactoring --topo-order logicDerrick Stolee via GitGitGadget, Aug 27, 2018
  6. 5/6 commit/revisions: bookkeeping before refactoringDerrick Stolee via GitGitGadget, Aug 27, 2018
  7. 6/6 revision.c: refactor basic topo-order logicDerrick Stolee via GitGitGadget, Aug 27, 2018
  8. Junio C HamanoAug 27, 2018
  9. 0/6 Use generation numbers for --topo-orderDerrick Stolee via GitGitGadget, Sep 18, 2018
  10. 1/6 prio-queue: add 'peek' operationDerrick Stolee via GitGitGadget, Sep 18, 2018
  11. 2/6 test-reach: add run_three_modes methodDerrick Stolee via GitGitGadget, Sep 18, 2018
  12. SZEDER GáborSep 18, 2018
  13. Junio C HamanoSep 19, 2018
  14. Junio C HamanoSep 19, 2018
  15. Junio C HamanoSep 20, 2018
  16. 3/6 test-reach: add rev-list testsDerrick Stolee via GitGitGadget, Sep 18, 2018
  17. 4/6 revision.c: begin refactoring --topo-order logicDerrick Stolee via GitGitGadget, Sep 18, 2018
  18. 5/6 commit/revisions: bookkeeping before refactoringDerrick Stolee via GitGitGadget, Sep 18, 2018
  19. 6/6 revision.c: refactor basic topo-order logicDerrick Stolee via GitGitGadget, Sep 18, 2018
  20. Ævar Arnfjörð BjarmasonSep 18, 2018
  21. Ævar Arnfjörð BjarmasonSep 18, 2018
  22. Derrick StoleeSep 21, 2018
  23. 0/7 Use generation numbers for --topo-orderDerrick Stolee via GitGitGadget, Sep 21, 2018
  24. 1/7 prio-queue: add 'peek' operationDerrick Stolee via GitGitGadget, Sep 21, 2018
  25. Derrick StoleeSep 26, 2018
  26. Jeff KingOct 11, 2018
  27. 2/7 test-reach: add run_three_modes methodDerrick Stolee via GitGitGadget, Sep 21, 2018
  28. Jeff KingOct 11, 2018
  29. 3/7 test-reach: add rev-list testsDerrick Stolee via GitGitGadget, Sep 21, 2018
  30. Jeff KingOct 11, 2018
  31. Junio C HamanoOct 12, 2018
  32. 4/7 revision.c: begin refactoring --topo-order logicDerrick Stolee via GitGitGadget, Sep 21, 2018
  33. Jeff KingOct 11, 2018
  34. Junio C HamanoOct 12, 2018
  35. Derrick StoleeOct 12, 2018
  36. Johannes SixtOct 12, 2018
  37. Junio C HamanoOct 13, 2018
  38. 5/7 commit/revisions: bookkeeping before refactoringDerrick Stolee via GitGitGadget, Sep 21, 2018
  39. Jeff KingOct 11, 2018
  40. 6/7 revision.h: add whitespace in flag definitionsDerrick Stolee via GitGitGadget, Sep 21, 2018
  41. 7/7 revision.c: refactor basic topo-order logicDerrick Stolee via GitGitGadget, Sep 21, 2018
  42. Derrick StoleeSep 27, 2018
  43. Jakub NarebskiOct 6, 2018
  44. Jeff KingOct 11, 2018
  45. Derrick StoleeOct 11, 2018
  46. Jeff KingOct 25, 2018
  47. Derrick StoleeOct 25, 2018
  48. Stefan BellerOct 11, 2018
  49. Junio C HamanoSep 21, 2018
  50. 0/7 Use generation numbers for --topo-orderDerrick Stolee via GitGitGadget, Oct 16, 2018
  51. 1/7 prio-queue: add 'peek' operationDerrick Stolee via GitGitGadget, Oct 16, 2018
  52. 3/7 test-reach: add rev-list testsDerrick Stolee via GitGitGadget, Oct 16, 2018
  53. Jakub NarebskiOct 21, 2018
  54. Derrick StoleeOct 21, 2018
  55. 4/7 revision.c: begin refactoring --topo-order logicDerrick Stolee via GitGitGadget, Oct 16, 2018
  56. Jakub NarebskiOct 21, 2018
  57. Junio C HamanoOct 22, 2018
  58. Derrick StoleeOct 22, 2018
  59. revision.c: use new algorithm in A..B caseDerrick Stolee, Oct 22, 2018
  60. Junio C HamanoOct 25, 2018
  61. Jakub NarebskiOct 26, 2018
  62. 2/7 test-reach: add run_three_modes methodDerrick Stolee via GitGitGadget, Oct 16, 2018
  63. 5/7 commit/revisions: bookkeeping before refactoringDerrick Stolee via GitGitGadget, Oct 16, 2018
  64. Jakub NarebskiOct 21, 2018
  65. 6/7 revision.c: generation-based topo-order algorithmDerrick Stolee via GitGitGadget, Oct 16, 2018
  66. Jakub NarebskiOct 22, 2018
  67. Derrick StoleeOct 23, 2018
  68. Jakub NarebskiOct 26, 2018
  69. 7/7 t6012: make rev-list tests more interestingDerrick Stolee via GitGitGadget, Oct 16, 2018
  70. Jakub NarebskiOct 23, 2018
  71. Jakub NarebskiOct 21, 2018
  72. Junio C HamanoNov 1, 2018
  73. Derrick StoleeNov 1, 2018
  74. Junio C HamanoNov 1, 2018
  75. 0/7 Use generation numbers for --topo-orderDerrick Stolee, Nov 1, 2018
  76. 1/7 prio-queue: add 'peek' operationDerrick Stolee, Nov 1, 2018
  77. 2/7 test-reach: add run_three_modes methodDerrick Stolee, Nov 1, 2018
  78. 3/7 test-reach: add rev-list testsDerrick Stolee, Nov 1, 2018
  79. 4/7 revision.c: begin refactoring --topo-order logicDerrick Stolee, Nov 1, 2018
  80. 5/7 commit/revisions: bookkeeping before refactoringDerrick Stolee, Nov 1, 2018
  81. 6/7 revision.c: generation-based topo-order algorithmDerrick Stolee, Nov 1, 2018
  82. SZEDER GáborNov 1, 2018
  83. Derrick StoleeNov 1, 2018
  84. Mike HommeyNov 8, 2019
  85. Derrick StoleeNov 11, 2019
  86. SZEDER GáborNov 18, 2019
  87. 7/7 t6012: make rev-list tests more interestingDerrick Stolee, Nov 1, 2018

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.