forked from git/git
-
Notifications
You must be signed in to change notification settings - Fork 187
commit-reach: replace queue_has_nonstale with a counter #2124
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
spkrka
wants to merge
3
commits into
gitgitgadget:master
Choose a base branch
from
spkrka:queue-has-nonstale-v3
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+43
−17
Open
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,8 +17,9 @@ | |
| #define PARENT2 (1u<<17) | ||
| #define STALE (1u<<18) | ||
| #define RESULT (1u<<19) | ||
| #define ENQUEUED (1u<<20) | ||
|
|
||
| static const unsigned all_flags = (PARENT1 | PARENT2 | STALE | RESULT); | ||
| static const unsigned all_flags = (PARENT1 | PARENT2 | STALE | RESULT | ENQUEUED); | ||
|
|
||
| static int compare_commits_by_gen(const void *_a, const void *_b) | ||
| { | ||
|
|
@@ -39,14 +40,25 @@ static int compare_commits_by_gen(const void *_a, const void *_b) | |
| return 0; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Derrick Stolee wrote on the Git mailing list (how to reply to this email): On 5/24/26 1:42 PM, Kristofer Karlsson via GitGitGadget wrote:
> From: Kristofer Karlsson <krka@spotify.com>
> > paint_down_to_common() terminates when every commit remaining in its
> priority queue is STALE. This was checked by queue_has_nonstale(),
> which performed an O(n) linear scan of the entire queue on every
> iteration, resulting in O(n*m) total overhead where n is the queue
> size and m is the number of commits processed.
> > Replace this with an O(1) nonstale_count that tracks the number of
> non-stale commits currently in the queue. The counter is incremented
> by maybe_enqueue() and decremented on dequeue and by mark_stale()
> when a commit transitions to STALE while still in the queue. Since
> each commit appears at most once (guaranteed by the ENQUEUED flag
> from the previous commit), the counter is exact.
This idea has a lot of merit, but I'm a bit concerned about the
organization of data. My ideas of how to improve things may also
impact patch 1's use of ENQUEUED.
> -static void maybe_enqueue(struct prio_queue *queue, struct commit *c)
> +static void maybe_enqueue(struct prio_queue *queue, struct commit *c,
> + int *nonstale_count)
> {
> if (c->object.flags & ENQUEUED)
> return;
> c->object.flags |= ENQUEUED;
> prio_queue_put(queue, c);
> + if (!(c->object.flags & STALE))
> + (*nonstale_count)++;
> +}
> +
> +static void mark_stale(struct commit *c, unsigned queued_flag,
> + int *nonstale_count)
> +{
> + if (!(c->object.flags & STALE)) {
> + if (c->object.flags & queued_flag)
> + (*nonstale_count)--;
> + c->object.flags |= STALE;
> + }
> }
These two methods have some concerns on my end:
1. We need to store the nonstale count somewhere other than the
priority queue, even though it's necessarily representing a
subset of the commits within the queue.
2. mark_stale() needs a queued_flag. (I need to check to see if
this is indeed changing in multiple callers or should always
be ENQUEUED).
> static int queue_has_nonstale(struct prio_queue *queue)
> @@ -68,6 +81,7 @@ static int paint_down_to_common(struct repository *r,
> {
> struct prio_queue queue = { compare_commits_by_gen_then_commit_date };
> int i;
> + int nonstale_count = 0;
My preference would be to create a new struct that contains a
prio_queue as a member _and_ a nonstale_count. It could initialize
with compare_commits_by_gen_then_commit_date by default.
The important thing is that consumers of such a "stale-tracking"
queue would not be setting the STALE or ENQUEUED bits themselves,
but instead the queue would be responsible for that.
This could allow us to simplify callers by always assuming we can
"add" an element to the queue and the queue will use its ENQUEUED
bit to prevent duplicates from reaching its internal prio_queue.
Such a data structure could be private to commit-reach.c for now,
since all the methods that would use it seem to be colocated there.
This is a big ask, but I'm interested to see if such an approach
would simplify things here.
Here's a potential breakdown of how to build such a thing in
"small" patches:
1. Create the data structure and update paint_down_to_common and
ahead_behind to use that structure, but still use the existing
prio_queue methods on its internal member.
2. Add the ENQUEUED bit and methods on the new struct that add
that bit as it adds commits to the inner prio_queue. It would
also ignore commits that already have that bit. (Should it
also remove the bit as commits are removed from the queue?)
3. Now add the nonstale_count (or stale count?) to the struct and
have it control the STALE bit modifications, with increasing
the stale count when ENQUEUED is live, and decreasing the stale
count as such a STALE object is dequeued.
I like the idea of this being encapsulated within the struct and
its helper methods. But the proof will be in the implementation.
Thanks,
-Stolee |
||
| } | ||
|
|
||
| static int queue_has_nonstale(struct prio_queue *queue) | ||
| static void maybe_enqueue(struct prio_queue *queue, struct commit *c, | ||
| int *nonstale_count) | ||
| { | ||
| for (size_t i = 0; i < queue->nr; i++) { | ||
| struct commit *commit = queue->array[i].data; | ||
| if (!(commit->object.flags & STALE)) | ||
| return 1; | ||
| if (c->object.flags & ENQUEUED) | ||
| return; | ||
| c->object.flags |= ENQUEUED; | ||
| prio_queue_put(queue, c); | ||
| if (!(c->object.flags & STALE)) | ||
| (*nonstale_count)++; | ||
| } | ||
|
|
||
| static void mark_stale(struct commit *c, unsigned queued_flag, | ||
| int *nonstale_count) | ||
| { | ||
| if (!(c->object.flags & STALE)) { | ||
| if (c->object.flags & queued_flag) | ||
| (*nonstale_count)--; | ||
| c->object.flags |= STALE; | ||
| } | ||
| return 0; | ||
| } | ||
|
|
||
| /* all input commits in one and twos[] must have been parsed! */ | ||
|
|
@@ -59,6 +71,7 @@ static int paint_down_to_common(struct repository *r, | |
| { | ||
| struct prio_queue queue = { compare_commits_by_gen_then_commit_date }; | ||
| int i; | ||
| int nonstale_count = 0; | ||
| timestamp_t last_gen = GENERATION_NUMBER_INFINITY; | ||
| struct commit_list **tail = result; | ||
|
|
||
|
|
@@ -70,19 +83,23 @@ static int paint_down_to_common(struct repository *r, | |
| commit_list_append(one, result); | ||
| return 0; | ||
| } | ||
| prio_queue_put(&queue, one); | ||
| maybe_enqueue(&queue, one, &nonstale_count); | ||
|
|
||
| for (i = 0; i < n; i++) { | ||
| twos[i]->object.flags |= PARENT2; | ||
| prio_queue_put(&queue, twos[i]); | ||
| maybe_enqueue(&queue, twos[i], &nonstale_count); | ||
| } | ||
|
|
||
| while (queue_has_nonstale(&queue)) { | ||
| while (nonstale_count > 0) { | ||
| struct commit *commit = prio_queue_get(&queue); | ||
| struct commit_list *parents; | ||
| int flags; | ||
| timestamp_t generation = commit_graph_generation(commit); | ||
|
|
||
| commit->object.flags &= ~ENQUEUED; | ||
| if (!(commit->object.flags & STALE)) | ||
| nonstale_count--; | ||
|
|
||
| if (min_generation && generation > last_gen) | ||
| BUG("bad generation skip %"PRItime" > %"PRItime" at %s", | ||
| generation, last_gen, | ||
|
|
@@ -123,8 +140,10 @@ static int paint_down_to_common(struct repository *r, | |
| return error(_("could not parse commit %s"), | ||
| oid_to_hex(&p->object.oid)); | ||
| } | ||
| if (flags & STALE) | ||
| mark_stale(p, ENQUEUED, &nonstale_count); | ||
| p->object.flags |= flags; | ||
| prio_queue_put(&queue, p); | ||
| maybe_enqueue(&queue, p, &nonstale_count); | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -1022,12 +1041,15 @@ struct commit_list *get_reachable_subset(struct commit **from, size_t nr_from, | |
| define_commit_slab(bit_arrays, struct bitmap *); | ||
| static struct bit_arrays bit_arrays; | ||
|
|
||
| static void insert_no_dup(struct prio_queue *queue, struct commit *c) | ||
| static void insert_no_dup(struct prio_queue *queue, struct commit *c, | ||
| int *nonstale_count) | ||
| { | ||
| if (c->object.flags & PARENT2) | ||
| return; | ||
| prio_queue_put(queue, c); | ||
| c->object.flags |= PARENT2; | ||
| if (!(c->object.flags & STALE)) | ||
| (*nonstale_count)++; | ||
| } | ||
|
|
||
| static struct bitmap *get_bit_array(struct commit *c, int width) | ||
|
|
@@ -1053,6 +1075,7 @@ void ahead_behind(struct repository *r, | |
| { | ||
| struct prio_queue queue = { .compare = compare_commits_by_gen_then_commit_date }; | ||
| size_t width = DIV_ROUND_UP(commits_nr, BITS_IN_EWORD); | ||
| int nonstale_count = 0; | ||
|
|
||
| if (!commits_nr || !counts_nr) | ||
| return; | ||
|
|
@@ -1071,14 +1094,17 @@ void ahead_behind(struct repository *r, | |
| struct bitmap *bitmap = get_bit_array(c, width); | ||
|
|
||
| bitmap_set(bitmap, i); | ||
| insert_no_dup(&queue, c); | ||
| insert_no_dup(&queue, c, &nonstale_count); | ||
| } | ||
|
|
||
| while (queue_has_nonstale(&queue)) { | ||
| while (nonstale_count > 0) { | ||
| struct commit *c = prio_queue_get(&queue); | ||
| struct commit_list *p; | ||
| struct bitmap *bitmap_c = get_bit_array(c, width); | ||
|
|
||
| if (!(c->object.flags & STALE)) | ||
| nonstale_count--; | ||
|
|
||
| for (size_t i = 0; i < counts_nr; i++) { | ||
| int reach_from_tip = !!bitmap_get(bitmap_c, counts[i].tip_index); | ||
| int reach_from_base = !!bitmap_get(bitmap_c, counts[i].base_index); | ||
|
|
@@ -1107,9 +1133,9 @@ void ahead_behind(struct repository *r, | |
| * queue is STALE. | ||
| */ | ||
| if (bitmap_popcount(bitmap_p) == commits_nr) | ||
| p->item->object.flags |= STALE; | ||
| mark_stale(p->item, PARENT2, &nonstale_count); | ||
|
|
||
| insert_no_dup(&queue, p->item); | ||
| insert_no_dup(&queue, p->item, &nonstale_count); | ||
| } | ||
|
|
||
| free_bit_array(c); | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Junio C Hamano wrote on the Git mailing list (how to reply to this email):
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Derrick Stolee wrote on the Git mailing list (how to reply to this email):