From: Jan Kara <jack@suse.cz>
To: git@vger.kernel.org
Cc: Jan Kara <jack@suse.cz>
Subject: [PATCH 19/27] bisect: Compute reachability of tested revs
Date: Thu, 18 Nov 2021 17:49:32 +0100 [thread overview]
Message-ID: <20211118164940.8818-20-jack@suse.cz> (raw)
In-Reply-To: <20211118164940.8818-1-jack@suse.cz>
Compute for each rev a bitmap of revs that can reach it and that were
tested during stochastic bisection run.
Signed-off-by: Jan Kara <jack@suse.cz>
---
bisect.c | 199 ++++++++++++++++++++++++++++++++++++++++++++++++++-
fixedpoint.h | 18 +++++
2 files changed, 215 insertions(+), 2 deletions(-)
diff --git a/bisect.c b/bisect.c
index 3a26255e8650..cf11926d6f4e 100644
--- a/bisect.c
+++ b/bisect.c
@@ -17,6 +17,7 @@
#include "object-store.h"
#include "dir.h"
#include "fixedpoint.h"
+#include "hashmap.h"
static struct oid_array good_revs;
static struct oid_array ptest_revs;
@@ -24,6 +25,16 @@ static struct oid_array skipped_revs;
static fpnum_t result_confidence;
+struct tested_rev {
+ struct hashmap_entry entry;
+ struct object_id *oid;
+ int index; /* Index in ptest_revs array */
+ fpnum_t confidence; /* Total confidence the rev is good computed
+ * from of all tests on this rev */
+};
+
+static struct hashmap tested_revs_map;
+
static struct object_id *current_bad_oid;
static const char *argv_checkout[] = {"checkout", "-q", NULL, "--", NULL};
@@ -80,8 +91,26 @@ static void clear_counted_flag(struct commit_list *list)
define_commit_slab(commit_weight, void *);
static struct commit_weight commit_weight;
+/*
+ * Information associated with each commit when computing stochastic bisection
+ * weights.
+ */
+struct st_weight {
+ fpnum_t weight; /* Total weight of all reachable commits */
+ fpnum_t node_weight; /* Weight of this particular commit */
+ unsigned long rev_bitmap[]; /* Bitmap of tested commits reaching
+ * this commit */
+};
+int sw_rev_bmp_longs;
+
#define DEBUG_BISECT 0
+#if DEBUG_BISECT > 0
+#define debug_bisect(fmt...) fprintf(stderr, fmt)
+#else
+#define debug_bisect(fmt...) do { } while (0)
+#endif
+
static inline int has_weight(struct commit_list *elem)
{
return commit_weight.slab_size > 0 &&
@@ -98,6 +127,37 @@ static inline void weight_set(struct commit_list *elem, int weight)
*(int *)*commit_weight_at(&commit_weight, elem->item) = weight;
}
+#define BITS_PER_LONG (sizeof(long) * 8)
+
+/*
+ * Add bits in 'from' reachability bitmap to 'to'. Returns 1 if any bit in 'to'
+ * was set.
+ */
+static int sw_rev_bmp_or(struct st_weight *to, struct st_weight *from)
+{
+ int i;
+ int set = 0;
+
+ for (i = 0; i < sw_rev_bmp_longs; i++) {
+ unsigned long prev = to->rev_bitmap[i];
+
+ to->rev_bitmap[i] |= from->rev_bitmap[i];
+ set |= (to->rev_bitmap[i] != prev);
+ }
+ return set;
+}
+
+static void sw_rev_bmp_set(struct st_weight *to, int idx)
+{
+ to->rev_bitmap[idx / BITS_PER_LONG] |= 1UL << (idx % BITS_PER_LONG);
+}
+
+static unsigned long sw_rev_bmp_test(struct st_weight *to, int idx)
+{
+ return to->rev_bitmap[idx / BITS_PER_LONG] &
+ (1UL << (idx % BITS_PER_LONG));
+}
+
static int count_interesting_parents(struct commit *commit, unsigned bisect_flags)
{
struct commit_list *p;
@@ -403,6 +463,14 @@ static void *setup_commit_weight_array(struct commit_list *list, int nodes)
struct commit_list *p;
int n;
+ /* Stochastic bisection? */
+ if (result_confidence) {
+ int revs = ptest_revs.nr + 1;
+
+ sw_rev_bmp_longs = (revs + BITS_PER_LONG - 1) / BITS_PER_LONG;
+ entry_size = sizeof(struct st_weight) +
+ sw_rev_bmp_longs * sizeof(long);
+ }
array = xcalloc(nodes, entry_size);
for (n = 0, p = list; p; p = p->next, n++) {
*commit_weight_at(&commit_weight, p->item) =
@@ -411,6 +479,91 @@ static void *setup_commit_weight_array(struct commit_list *list, int nodes)
return array;
}
+static struct tested_rev *lookup_tested_oid(struct object_id *oid)
+{
+ struct tested_rev key;
+
+ hashmap_entry_init(&key.entry, oidhash(oid));
+ key.oid = oid;
+ return hashmap_get_entry(&tested_revs_map, &key, entry, NULL);
+}
+
+static int sw_rev_test_idx(struct commit *commit)
+{
+ struct tested_rev *tr;
+
+ tr = lookup_tested_oid(&commit->object.oid);
+ if (!tr)
+ return -1;
+ return tr->index;
+}
+
+static int propagate_to_ancestors(struct commit *commit)
+{
+ struct commit_list *q;
+ int out_of_order = 0, set;
+ struct st_weight *cw;
+
+ cw = *commit_weight_at(&commit_weight, commit);
+ for (q = commit->parents; q; q = q->next) {
+ if (q->item->object.flags & UNINTERESTING)
+ continue;
+ set = sw_rev_bmp_or(*commit_weight_at(&commit_weight, q->item),
+ cw);
+ if (set && q->item->object.flags & COUNTED) {
+ debug_bisect("descendants_bitmap: Recursion for %s\n",
+ oid_to_hex(&q->item->object.oid));
+ /*
+ * If the commit is already processed, we need to
+ * propagate bitmap update to all already processed
+ * ancestors. The 'list' should be close to inverse
+ * topological order so this should be rare.
+ */
+ out_of_order = 1;
+ }
+ }
+ return out_of_order;
+}
+
+/*
+ * For each commit in the 'list' compute bitmap of tested revisions that can
+ * reach it. Note for our purposes each commit can reach itself.
+ */
+static void compute_tested_descendants(struct commit_list *list)
+{
+ struct commit_list *p;
+ int retry = 0, tried = 1;
+
+ for (p = list; p; p = p->next) {
+ struct commit *commit = p->item;
+ struct st_weight *cw;
+ int idx;
+
+ cw = *commit_weight_at(&commit_weight, commit);
+ idx = sw_rev_test_idx(commit);
+ if (idx >= 0)
+ sw_rev_bmp_set(cw, idx);
+ retry = propagate_to_ancestors(commit);
+ commit->object.flags |= COUNTED;
+ }
+ clear_counted_flag(list);
+ /*
+ * We expect the 'list' to be close to reverse topological order. If
+ * it is not exactly in reverse topological order, we need to repeat
+ * descendant bit propagation until bitmaps are stable.
+ */
+ while (retry) {
+ tried++;
+ retry = 0;
+ for (p = list; p; p = p->next) {
+ retry = propagate_to_ancestors(p->item);
+ p->item->object.flags |= COUNTED;
+ }
+ clear_counted_flag(list);
+ }
+ debug_bisect("%s: Took %d iterations to stabilize.\n", __func__, tried);
+}
+
static struct commit_list *reverse_list(struct commit_list *list)
{
struct commit_list *p, *next, *last = NULL;
@@ -459,6 +612,8 @@ void find_bisection(struct commit_list **commit_list, int *reaches,
goto out_weights;
}
weights = setup_commit_weight_array(list, on_list);
+ if (result_confidence)
+ compute_tested_descendants(list);
list = reverse_list(list);
show_list("bisection 2 sorted", 0, nr, list);
@@ -522,11 +677,39 @@ static GIT_PATH_FUNC(git_path_bisect_confidences, "BISECT_CONFIDENCES")
static GIT_PATH_FUNC(git_path_bisect_result_confidence, "BISECT_RESULT_CONFIDENCE")
static GIT_PATH_FUNC(git_path_head_name, "head-name")
+static int tested_rev_cmp(const void *data, const struct hashmap_entry *ap,
+ const struct hashmap_entry *bp, const void *keydata)
+{
+ const struct tested_rev *a, *b;
+
+ a = container_of(ap, const struct tested_rev, entry);
+ b = container_of(bp, const struct tested_rev, entry);
+
+ return oidcmp(a->oid, b->oid);
+}
+
+static void setup_tested_revs_map(void)
+{
+ int i;
+ struct tested_rev *tr;
+
+ hashmap_init(&tested_revs_map, tested_rev_cmp, NULL, ptest_revs.nr + 1);
+ for (i = 0; i < ptest_revs.nr; i++) {
+ tr = xmalloc(sizeof(struct tested_rev));
+ hashmap_entry_init(&tr->entry, oidhash(ptest_revs.oid + i));
+ tr->oid = ptest_revs.oid + i;
+ tr->index = i;
+ tr->confidence = FP_HALF;
+ hashmap_add(&tested_revs_map, &tr->entry);
+ }
+}
+
static void read_bisect_confidences(void)
{
struct strbuf str = STRBUF_INIT;
const char *filename = git_path_bisect_result_confidence();
FILE *fp = fopen(filename, "r");
+ struct tested_rev *trev;
/* Just a regular bisection? */
if (!fp)
@@ -535,6 +718,8 @@ static void read_bisect_confidences(void)
die(_("Cannot parse result confidence in file '%s'"), filename);
fclose(fp);
+ setup_tested_revs_map();
+
/* No uncertain bisection steps yet? */
filename = git_path_bisect_confidences();
fp = fopen(filename, "r");
@@ -542,7 +727,7 @@ static void read_bisect_confidences(void)
return;
while (strbuf_getline_lf(&str, fp) != EOF) {
- fpnum_t prob;
+ fpnum_t prob, pos_prob, neg_prob;
char *spc;
char state;
struct object_id oid;
@@ -562,7 +747,17 @@ static void read_bisect_confidences(void)
if (get_oid(str.buf, &oid))
die(_("Cannot get oid of rev '%s' from file '%s'"),
str.buf, filename);
- /* We'll use parsed data later */
+ trev = lookup_tested_oid(&oid);
+ if (!trev)
+ die(_("Rev '%s' not tracked as ptest rev."), str.buf);
+ if (state == 'b')
+ prob = FP_ONE - prob;
+ pos_prob = fp_mul(trev->confidence, prob);
+ neg_prob = fp_mul(FP_ONE - trev->confidence, FP_ONE - prob);
+ trev->confidence = fp_div(pos_prob, pos_prob + neg_prob);
+ debug_bisect("read confidence %s: %"FPNUM_FMT
+ " (total %"FPNUM_FMT")\n",
+ str.buf, prob, trev->confidence);
}
strbuf_release(&str);
fclose(fp);
diff --git a/fixedpoint.h b/fixedpoint.h
index 3f6234c6530a..159bb6ef4358 100644
--- a/fixedpoint.h
+++ b/fixedpoint.h
@@ -27,4 +27,22 @@ static inline const double fp_to_double(fpnum_t n)
#define FP_ONE frac_to_fp(1, 1)
#define FP_HALF frac_to_fp(1, 2)
+/* Multiplication for numbers <= 1 */
+static inline const fpnum_t fp_mul(fpnum_t n, fpnum_t m)
+{
+ if (m == FP_ONE)
+ return n;
+ if (n == FP_ONE)
+ return m;
+ assert(m < FP_ONE && n < FP_ONE);
+ return (m * n) >> FIXEDPOINT_SHIFT;
+}
+
+/* Division for number <= 1 */
+static inline const fpnum_t fp_div(fpnum_t n, fpnum_t d)
+{
+ assert(n <= FP_ONE);
+ return (n << (FIXEDPOINT_SHIFT - 1)) / (d >> 1);
+}
+
#endif
--
2.26.2
next prev parent reply other threads:[~2021-11-18 16:50 UTC|newest]
Thread overview: 43+ messages / expand[flat|nested] mbox.gz Atom feed top
2021-11-18 16:49 Stochastic bisection support Jan Kara
2021-11-18 16:49 ` [PATCH 01/27] bisect: Fixup test rev-list-bisect/02 Jan Kara
2021-11-18 20:08 ` Chris Torek
2021-11-19 16:31 ` Johannes Schindelin
2021-11-22 12:48 ` Jan Kara
2021-11-18 16:49 ` [PATCH 02/27] bisect: Fixup bisect-porcelain/17 Jan Kara
2021-11-18 22:05 ` Taylor Blau
2021-11-22 12:27 ` Jan Kara
2021-11-18 16:49 ` [PATCH 03/27] bisect: Fixup test bisect-porcelain/20 Jan Kara
2021-11-18 20:13 ` Chris Torek
2021-11-18 22:10 ` Taylor Blau
2021-11-22 12:49 ` Jan Kara
2021-11-18 16:49 ` [PATCH 04/27] bisect: Fixup bisect-porcelain/32 Jan Kara
2021-11-18 16:49 ` [PATCH 05/27] bisect: Fixup bisect-porcelain/34 Jan Kara
2021-11-18 16:49 ` [PATCH 06/27] bisect: Fixup bisect-porcelain/40 Jan Kara
2021-11-18 16:49 ` [PATCH 07/27] bisect: Remove duplicated bisect-porcelain/48 Jan Kara
2021-11-18 16:49 ` [PATCH 08/27] bisect: Fixup bisect-porcelain/50 Jan Kara
2021-11-18 16:49 ` [PATCH 09/27] bisect: Fixup bisect-porcelain/54 Jan Kara
2021-11-18 16:49 ` [PATCH 10/27] bisect: Fixup bisect-porcelain/58 Jan Kara
2021-11-18 16:49 ` [PATCH 11/27] bisect: Fix bisection debugging Jan Kara
2021-11-18 16:49 ` [PATCH 12/27] bisect: Accept and store confidence with each decision Jan Kara
2021-11-18 16:49 ` [PATCH 13/27] bisect: Allow specifying desired result confidence Jan Kara
2021-11-18 16:49 ` [PATCH 14/27] bisect: Use void * for commit_weight Jan Kara
2021-11-18 16:49 ` [PATCH 15/27] bisect: Rename clear_distance() to clear_counted_flag() Jan Kara
2021-11-18 16:49 ` [PATCH 16/27] bisect: Separate commit list reversal Jan Kara
2021-11-18 16:49 ` [PATCH 17/27] bisect: Allow more complex commit weights Jan Kara
2021-11-18 16:49 ` [PATCH 18/27] bisect: Terminate early if there are no eligible commits Jan Kara
2021-11-18 16:49 ` Jan Kara [this message]
2021-11-18 16:49 ` [PATCH 20/27] bisect: Compute probability a particular commit is bad Jan Kara
2021-11-18 16:49 ` [PATCH 21/27] bisect: Reorganize commit weight computation Jan Kara
2021-11-18 16:49 ` [PATCH 22/27] bisect: Move count_distance() Jan Kara
2021-11-18 16:49 ` [PATCH 23/27] bisect: Find bisection point for stochastic weights Jan Kara
2021-11-18 16:49 ` [PATCH 24/27] bisect: Stop bisection when we are confident about bad commit Jan Kara
2021-11-18 16:49 ` [PATCH 25/27] bisect: Report commit with the highest probability Jan Kara
2021-11-18 16:49 ` [PATCH 26/27] bisect: Debug stochastic bisection Jan Kara
2021-11-18 16:49 ` [PATCH 27/27] bisect: Allow bisection debugging of approx_halfway() Jan Kara
2021-11-18 22:49 ` Stochastic bisection support Taylor Blau
2021-11-22 12:13 ` Jan Kara
2021-11-19 16:39 ` Johannes Schindelin
2021-11-20 7:54 ` Chris Torek
2021-11-22 11:57 ` Jan Kara
2021-11-22 12:55 ` Christian Couder
2021-11-22 13:31 ` Jan Kara
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=20211118164940.8818-20-jack@suse.cz \
--to=jack@suse.cz \
--cc=git@vger.kernel.org \
/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).