Skip to content

Commit ebc9529

Browse files
chriscoolgitster
authored andcommitted
bisect: use a PRNG with a bias when skipping away from untestable commits
Using a PRNG (pseudo random number generator) with a bias should be better than alternating between 3 fixed ratios. In repositories with many untestable commits it should prevent alternating between areas where many commits are untestable. The bias should favor commits that can give more information, so that the bisection process should not loose much efficiency. HPA suggested to use a PRNG and found that the best bias is to raise a ratio between 0 and 1 given by the PRNG to the power 1.5. An integer square root function is implemented to avoid including <math.h> and linking with -lm. A PRNG function is implemented to get the same number sequence on different machines as suggested by "man 3 rand". Signed-off-by: Christian Couder <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent a66037c commit ebc9529

File tree

2 files changed

+41
-13
lines changed

2 files changed

+41
-13
lines changed

bisect.c

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -585,16 +585,49 @@ struct commit_list *filter_skipped(struct commit_list *list,
585585
return filtered;
586586
}
587587

588-
static struct commit_list *apply_skip_ratio(struct commit_list *list,
589-
int count,
590-
int skip_num, int skip_denom)
588+
#define PRN_MODULO 32768
589+
590+
/*
591+
* This is a pseudo random number generator based on "man 3 rand".
592+
* It is not used properly because the seed is the argument and it
593+
* is increased by one between each call, but that should not matter
594+
* for this application.
595+
*/
596+
int get_prn(int count) {
597+
count = count * 1103515245 + 12345;
598+
return ((unsigned)(count/65536) % PRN_MODULO);
599+
}
600+
601+
/*
602+
* Custom integer square root from
603+
* http://en.wikipedia.org/wiki/Integer_square_root
604+
*/
605+
static int sqrti(int val)
606+
{
607+
float d, x = val;
608+
609+
if (val == 0)
610+
return 0;
611+
612+
do {
613+
float y = (x + (float)val / x) / 2;
614+
d = (y > x) ? y - x : x - y;
615+
x = y;
616+
} while (d >= 0.5);
617+
618+
return (int)x;
619+
}
620+
621+
static struct commit_list *skip_away(struct commit_list *list, int count)
591622
{
592-
int index, i;
593623
struct commit_list *cur, *previous;
624+
int prn, index, i;
625+
626+
prn = get_prn(count);
627+
index = (count * prn / PRN_MODULO) * sqrti(prn) / sqrti(PRN_MODULO);
594628

595629
cur = list;
596630
previous = NULL;
597-
index = count * skip_num / skip_denom;
598631

599632
for (i = 0; cur; cur = cur->next, i++) {
600633
if (i == index) {
@@ -614,7 +647,6 @@ static struct commit_list *managed_skipped(struct commit_list *list,
614647
struct commit_list **tried)
615648
{
616649
int count, skipped_first;
617-
int skip_num, skip_denom;
618650

619651
*tried = NULL;
620652

@@ -626,11 +658,7 @@ static struct commit_list *managed_skipped(struct commit_list *list,
626658
if (!skipped_first)
627659
return list;
628660

629-
/* Use alternatively 1/5, 2/5 and 3/5 as skip ratio. */
630-
skip_num = count % 3 + 1;
631-
skip_denom = 5;
632-
633-
return apply_skip_ratio(list, count, skip_num, skip_denom);
661+
return skip_away(list, count);
634662
}
635663

636664
static void bisect_rev_setup(struct rev_info *revs, const char *prefix,

t/t6030-bisect-porcelain.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -563,8 +563,8 @@ test_expect_success 'skipping away from skipped commit' '
563563
hash7=$(git rev-parse --verify HEAD) &&
564564
test "$hash7" = "$HASH7" &&
565565
git bisect skip &&
566-
hash3=$(git rev-parse --verify HEAD) &&
567-
test "$hash3" = "$HASH3"
566+
para3=$(git rev-parse --verify HEAD) &&
567+
test "$para3" = "$PARA_HASH3"
568568
'
569569

570570
#

0 commit comments

Comments
 (0)