cmd/internal/obj/arm64, image/gif, runtime, sort: use math/bits to calculate log2

In several places the integer log2 is calculated using loops or similar
mechanisms. math/bits.Len* provide a simpler and more efficient
mechanisms for this.

Annoyingly, every usage has slightly different ideas of what "log2"
means and how non-positive inputs should be handled. I verified the
replacements in each case by comparing the result for inputs from 0
to 1<<16.

Change-Id: Ie962a74674802da363e0038d34c06979ccb41cf3
Reviewed-on: https://go-review.googlesource.com/c/go/+/721880
Reviewed-by: Mark Freeman <markfreeman@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
This commit is contained in:
Axel Wagner 2025-11-19 09:28:16 +01:00 committed by Sean Liao
parent 437323ef7b
commit a18294bb6a
4 changed files with 15 additions and 46 deletions

View file

@ -1674,32 +1674,7 @@ func log2(x uint64) uint32 {
if x == 0 { if x == 0 {
panic("log2 of 0") panic("log2 of 0")
} }
n := uint32(0) return uint32(bits.Len64(x) - 1)
if x >= 1<<32 {
x >>= 32
n += 32
}
if x >= 1<<16 {
x >>= 16
n += 16
}
if x >= 1<<8 {
x >>= 8
n += 8
}
if x >= 1<<4 {
x >>= 4
n += 4
}
if x >= 1<<2 {
x >>= 2
n += 2
}
if x >= 1<<1 {
x >>= 1
n += 1
}
return n
} }
func autoclass(l int64) int { func autoclass(l int64) int {

View file

@ -15,6 +15,7 @@ import (
"image/draw" "image/draw"
"internal/byteorder" "internal/byteorder"
"io" "io"
"math/bits"
) )
// Graphic control extension fields. // Graphic control extension fields.
@ -23,15 +24,11 @@ const (
gcBlockSize = 0x04 gcBlockSize = 0x04
) )
var log2Lookup = [8]int{2, 4, 8, 16, 32, 64, 128, 256}
func log2(x int) int { func log2(x int) int {
for i, v := range log2Lookup { if x < 2 {
if x <= v { return 0
return i
}
} }
return -1 return bits.Len(uint(x-1)) - 1
} }
// writer is a buffered writer. // writer is a buffered writer.
@ -192,7 +189,7 @@ func (e *encoder) writeHeader() {
} }
func encodeColorTable(dst []byte, p color.Palette, size int) (int, error) { func encodeColorTable(dst []byte, p color.Palette, size int) (int, error) {
if uint(size) >= uint(len(log2Lookup)) { if uint(size) >= 8 {
return 0, errors.New("gif: cannot encode color table with more than 256 entries") return 0, errors.New("gif: cannot encode color table with more than 256 entries")
} }
for i, c := range p { for i, c := range p {
@ -212,7 +209,7 @@ func encodeColorTable(dst []byte, p color.Palette, size int) (int, error) {
dst[3*i+1] = g dst[3*i+1] = g
dst[3*i+2] = b dst[3*i+2] = b
} }
n := log2Lookup[size] n := 1 << (size + 1)
if n > len(p) { if n > len(p) {
// Pad with black. // Pad with black.
clear(dst[3*len(p) : 3*n]) clear(dst[3*len(p) : 3*n])

View file

@ -12,6 +12,7 @@ import (
"internal/runtime/atomic" "internal/runtime/atomic"
"internal/runtime/gc" "internal/runtime/gc"
"internal/runtime/sys" "internal/runtime/sys"
"math/bits"
"unsafe" "unsafe"
) )
@ -181,12 +182,10 @@ func stackinit() {
// stacklog2 returns ⌊log_2(n)⌋. // stacklog2 returns ⌊log_2(n)⌋.
func stacklog2(n uintptr) int { func stacklog2(n uintptr) int {
log2 := 0 if n == 0 {
for n > 1 { return 0
n >>= 1
log2++
} }
return log2 return bits.Len64(uint64(n))
} }
// Allocates a stack from the free pool. Must be called with // Allocates a stack from the free pool. Must be called with

View file

@ -5,6 +5,7 @@
package sort_test package sort_test
import ( import (
"math/bits"
"runtime" "runtime"
. "sort" . "sort"
stringspkg "strings" stringspkg "strings"
@ -135,13 +136,10 @@ func TestFind(t *testing.T) {
// log2 computes the binary logarithm of x, rounded up to the next integer. // log2 computes the binary logarithm of x, rounded up to the next integer.
// (log2(0) == 0, log2(1) == 0, log2(2) == 1, log2(3) == 2, etc.) // (log2(0) == 0, log2(1) == 0, log2(2) == 1, log2(3) == 2, etc.)
func log2(x int) int { func log2(x int) int {
n := 0 if x < 1 {
for p := 1; p < x; p += p { return 0
// p == 2**n
n++
} }
// p/2 < x <= p == 2**n return bits.Len(uint(x - 1))
return n
} }
func TestSearchEfficiency(t *testing.T) { func TestSearchEfficiency(t *testing.T) {