From the public Gopher points north

Brothers, please tell me the trouble, the front is a large-scale installation scene.


First of all, Xu would like to thank others for their approval. This is the motivation for me to like this. At the same time, I also need to reflect. The little sister is more tactful, but our Sichuan words, previous article title is really cuo .

, and the exclamation mark double-linked the name "1610ff180100b0 Surprise! There is such an exquisite little function in Go! ". Let's take a look at some small functions that are not so consistent with the title.

returns a/b rounded up to the nearest integer

func divRoundUp(n, a uintptr) uintptr {
    return (n + a - 1) / a
}

There should be a lot of people who have used this method, the most typical is paging calculation.

Determine whether x is the n-th power of 2

func isPowerOfTwo(x uintptr) bool {
    return x&(x-1) == 0
}

This is also quite easy to understand, the only thing to note is that x needs to be greater than 0, because the equation 0 is also true.

Up/down round x to a multiple of a, and a must be 2 to the power of n

// 向上将x舍入为a的倍数,例如:x=6,a=4则返回值为8
func alignUp(x, a uintptr) uintptr {
    return (x + a - 1) &^ (a - 1)
}

// 向上将x舍入为a的倍数,例如:x=6,a=4则返回值为4
func alignDown(x, a uintptr) uintptr {
    return x &^ (a - 1)
}

Here, Lao Xu once again clarified a concept, 2 to the n-th power is 1 left shifted by n bits. Then in the above code, ^ is the bit- ^ (a - 1) inversion of the monocular algorithm, and the result of the operation of 0610ff18010197 is that all bits except the lowest n bits are 0. The remaining part is a simple addition and subtraction operation and bitwise AND.

The above code looks at each part of the code separately, and when put together, it looks dumbfounded. Fortunately, after Lao Xu's unremitting efforts, he finally found an understandable way.

Take x=10,a=4 as an example. a is 2 to the power of 2, that is, 1 is shifted to the left by 2 bits. x two parts, the first part x1 is 0b1000 , and the second part x2 is 0b0011 . x split mode 1 is left n bits obtained a determined, i.e., the lowest n bits of x is x2, x1 was x-x2. Therefore, x1 is equivalent to 0b10 shifted by 2 bits left, that is, x1 is already an integer multiple of a. At this time, as long as x2 is greater than 0, x2+a-1 will definitely move forward by 1. x1+1 or x1 not x rounded up to a For integer multiples, and finally with ^ (a - 1) to clear the lowest 2 bits to get the final return result.

There is one thing to say, I definitely can't write such a logic, which also makes me have to sigh that the big guys have a superb understanding of computers. Such a function is awesome, but it is still used as little as possible in actual development. One is that there are restrictions on the use scene (a must be the n-th power of 2), and the other is not easy to understand, except for dazzling skills and pretenders (except for extremely high performance requirements).

Boolean conversion shaping

// bool2int returns 0 if x is false or 1 if x is true.
func bool2int(x bool) int {
    return int(uint8(*(*uint8)(unsafe.Pointer(&x))))
}

If I were to write this function, an ordinary 0610ff18010261 would be done, and now I have switch Lao Xu here specially reminds that byte slices and strings can also be converted into each other in the above-mentioned way.

Calculate the number of digits in the lowest 0 of different types

var ntz8tab = [256]uint8{
    0x08, ..., 0x00,
}
// Ctz8 returns the number of trailing zero bits in x; the result is 8 for x == 0.
func Ctz8(x uint8) int {
    return int(ntz8tab[x])
}

const deBruijn32ctz = 0x04653adf

var deBruijnIdx32ctz = [32]byte{
    0, 1, 2, 6, 3, 11, 7, 16,
    4, 14, 12, 21, 8, 23, 17, 26,
    31, 5, 10, 15, 13, 20, 22, 25,
    30, 9, 19, 24, 29, 18, 28, 27,
}

// Ctz32 counts trailing (low-order) zeroes,
// and if all are zero, then 32.
func Ctz32(x uint32) int {
    x &= -x                       // isolate low-order bit
    y := x * deBruijn32ctz >> 27  // extract part of deBruijn sequence
    i := int(deBruijnIdx32ctz[y]) // convert to bit index
    z := int((x - 1) >> 26 & 32)  // adjustment if zero
    return i + z
}

const deBruijn64ctz = 0x0218a392cd3d5dbf

var deBruijnIdx64ctz = [64]byte{
    0, 1, 2, 7, 3, 13, 8, 19,
    4, 25, 14, 28, 9, 34, 20, 40,
    5, 17, 26, 38, 15, 46, 29, 48,
    10, 31, 35, 54, 21, 50, 41, 57,
    63, 6, 12, 18, 24, 27, 33, 39,
    16, 37, 45, 47, 30, 53, 49, 56,
    62, 11, 23, 32, 36, 44, 52, 55,
    61, 22, 43, 51, 60, 42, 59, 58,
}

// Ctz64 counts trailing (low-order) zeroes,
// and if all are zero, then 64.
func Ctz64(x uint64) int {
    x &= -x                       // isolate low-order bit
    y := x * deBruijn64ctz >> 58  // extract part of deBruijn sequence
    i := int(deBruijnIdx64ctz[y]) // convert to bit index
    z := int((x - 1) >> 57 & 64)  // adjustment if zero
    return i + z
}

Ctz8 , Ctz32 and Ctz64 calculate the number of unsigned 8, 32, and 64 bits whose lowest bit is 0, that is, the number of bits shifted to the left of a certain number.

The function of the function can be understood through translation, and I can also deeply understand that this is a typical space for time, but I have to ask why I can't answer it. But Old Xu has already found the answer for you, and the answer is hidden in this Using de Bruijn Sequences to Index a 1 in a Computer Word paper. Welcome the giants to challenge, and I just want to sit back and enjoy the success, so before the giants finish analyzing this paper, let these functions settle in my favorites bar to show off their skills later.

It is specifically stated here that there is a specialization in the surgical industry, and we don't necessarily have to be good at everything, but we must know as much as possible that there is such a thing. This is an interface that Old Xu found for himself not to study this paper, and it is also one of the meanings of writing this article (in case someone mentions the Bruijn Sequences , we will not be overly ignorant).

Some functions in the math/bits package

If anyone knows this package, please forgive my ignorance and just skip this part. Old Xu found that the package is derived from ntz8tab where the file variable runtime/internal/sys/intrinsics_common.go an annotation.

// Copied from math/bits to avoid dependence.

As a senior CV engineer, my first reaction after seeing this sentence is that I can finally straighten my waist. Appropriate Copy code is not ashamed!

math/bits There are many functions in this package, so I can just pick a few introductions, and please readers to dig for the rest.

LeadingZeros(x uint) int : Returns the number of all high bits of x that are 0.

TrailingZeros(x uint) int : Returns the number of the lowest bit of x being 0.

OnesCount(x uint) int : Returns the number of bits with 1 in x.

Reverse(x uint) uint : Reverse x by bit and then return.

Len(x uint) int : Returns the number of valid bits representing x (the high bits are not counted).

ReverseBytes(x uint) uint : Return x in reverse order in groups of 8 digits.

escapes x to the heap

// Dummy annotation marking that the value x escapes,
// for use in cases where the reflect code is so clever that
// the compiler cannot follow.
func escapes(x interface{}) {
    if dummy.b {
        dummy.x = x
    }
}

var dummy struct {
    b bool
    x interface{}
}

Old Xu reflect.ValueOf function, which was quite interesting at the time. Looking back now, I still admire it. Reading is a dialogue with the author, and reading the source code is a dialogue with the developer. Seeing this function is like seeing the Go language developers fighting the compiler.

Give up the current Processor


// Gosched yields the processor, allowing other goroutines to run. It does not
// suspend the current goroutine, so execution resumes automatically.
func Gosched() {
    checkTimeouts()
    mcall(gosched_m)
}

Give up the current Processor and allow other goroutines to execute. In the actual development, Lao Xu has not encountered a scene that needs to use this function, but it is always safe to know more.

Finally, I sincerely hope that this article can be helpful to readers.

Note :

  1. At the time of writing this article, the version of go used by the author is: go1.16.6

Gopher指北
158 声望1.7k 粉丝