Skip to content

Commit d343068

Browse files
pks-tgitster
authored andcommitted
wrapper: introduce log2u()
We have an implementation of a function that computes the log2 for an integer. While we could instead use log2(3P), that involves floating point numbers and is thus needlessly complex and inefficient. We're about to add a second caller that wants to compute log2 for a `size_t`. Let's thus move the function into "wrapper.h" such that it becomes generally available. While at it, tweak the implementation a bit: - The parameter is converted from `int` to `uintmax_t`. This conversion is safe to do in "bisect.c" because we already check that the argument is positive. - The return value is an `unsigned`. It cannot ever be negative, so it is pointless for it to be a signed integer. - Loop until `!n` instead of requiring that `n > 1` and then subtract 1 from the result and add a special case for `!sz`. This helps compilers to generate more efficient code. Compilers recognize the pattern of this function and optimize accordingly. On GCC 14.2 x86_64: log2u(unsigned long): test rdi, rdi je .L3 bsr rax, rdi ret .L3: mov eax, -1 ret Clang 18.1 does not yet recognize the pattern, but starts to do so on Clang trunk x86_64. The code isn't quite as efficient as the one generated by GCC, but still manages to optimize away the loop: log2u(unsigned long): test rdi, rdi je .LBB0_1 shr rdi bsr rcx, rdi mov eax, 127 cmovne rax, rcx xor eax, -64 add eax, 65 ret .LBB0_1: mov eax, -1 ret The pattern is also recognized on other platforms like ARM64 GCC 14.2.0, where we end up using `clz`: log2u(unsigned long): clz x2, x0 cmp x0, 0 mov w1, 63 sub w0, w1, w2 csinv w0, w0, wzr, ne ret Note that we have a similar function `fastlog2()` in the reftable code. As that codebase is separate from the Git codebase we do not adapt it to use the new function. Signed-off-by: Patrick Steinhardt <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 39bf06a commit d343068

File tree

2 files changed

+19
-11
lines changed

2 files changed

+19
-11
lines changed

bisect.c

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1130,16 +1130,6 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix)
11301130
return res;
11311131
}
11321132

1133-
static inline int log2i(int n)
1134-
{
1135-
int log2 = 0;
1136-
1137-
for (; n > 1; n >>= 1)
1138-
log2++;
1139-
1140-
return log2;
1141-
}
1142-
11431133
static inline int exp2i(int n)
11441134
{
11451135
return 1 << n;
@@ -1162,7 +1152,7 @@ int estimate_bisect_steps(int all)
11621152
if (all < 3)
11631153
return 0;
11641154

1165-
n = log2i(all);
1155+
n = log2u(all);
11661156
e = exp2i(n);
11671157
x = all - e;
11681158

wrapper.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,4 +140,22 @@ int csprng_bytes(void *buf, size_t len);
140140
*/
141141
uint32_t git_rand(void);
142142

143+
/* Provide log2 of the given `size_t`. */
144+
static inline unsigned log2u(uintmax_t sz)
145+
{
146+
unsigned l = 0;
147+
148+
/*
149+
* Technically this isn't required, but it helps the compiler optimize
150+
* this to a `bsr` instruction.
151+
*/
152+
if (!sz)
153+
return 0;
154+
155+
for (; sz; sz >>= 1)
156+
l++;
157+
158+
return l - 1;
159+
}
160+
143161
#endif /* WRAPPER_H */

0 commit comments

Comments
 (0)