如何在go中检查[]byte是否全为零

utugiqy6  于 2023-08-01  发布在  Go
关注(0)|答案(4)|浏览(207)

有没有一种方法可以在不检查每个元素或使用reflect的情况下检查字节片是空还是0?

theByteVar := make([]byte, 128)

if "theByteVar is empty or zeroes" {
   doSomething()
}

字符串
我发现的一个看起来很奇怪的解决方案是保留一个空字节数组用于比较。

theByteVar := make([]byte, 128)
emptyByteVar := make([]byte, 128)

// fill with anything
theByteVar[1] = 2

if reflect.DeepEqual(theByteVar,empty) == false {
   doSomething(theByteVar)
}


肯定有更好/更快的解决方案。
谢啦,谢啦
UPDATE对1000个循环做了一些比较,反射方式是目前为止最差的...

Equal Loops: 1000 in true in 19.197µs
Contains Loops: 1000 in true in 34.507µs
AllZero Loops: 1000 in true in 117.275µs
Reflect Loops: 1000 in true in 14.616277ms

frebpwbc

frebpwbc1#

将它与另一个只包含零的切片进行比较,这需要阅读(并比较)2个切片。
在这里使用一个for循环会更有效:

for _, v := range theByteVar {
    if v != 0 {
        doSomething(theByteVar)
        break
    }
}

字符串
如果你确实需要在多个地方使用它,请将它 Package 在一个实用程序函数中:

func allZero(s []byte) bool {
    for _, v := range s {
        if v != 0 {
            return false
        }
    }
    return true
}


然后使用它:

if !allZero(theByteVar) {
    doSomething(theByteVar)
}

lmvvr0a8

lmvvr0a82#

另一个解决方案借用了C.它可以通过使用Go中的unsafe包来实现。
这个想法很简单,我们可以在每个步骤中检查byte[i:i+8]的值,而不是检查[]byte中的每个字节。通过这样做,我们可以在每次迭代中检查8个字节,而不是只检查一个字节。
下面的代码不是最佳实践,只是展示了想法。

func IsAllBytesZero(data []byte) bool {
    n := len(data)

    // Round n down to the nearest multiple of 8
    // by clearing the last 3 bits.
    nlen8 := n & ^0b111
    i := 0

    for ; i < nlen8; i += 8 {
        b := *(*uint64)(unsafe.Pointer(&data[i]))
        if b != 0 {
            return false
        }
    }

    for ; i < n; i++ {
        if data[i] != 0 {
            return false
        }
    }

    return true
}

字符串

基准测试

测试用例:

仅测试最差情况(所有元素均为零)

方法:

  • IsAllBytesZero:unsafe封装解决方案
  • NaiveCheckAllBytesAreZero:一个循环来迭代整个字节数组并检查它。
  • CompareAllBytesWithFixedEmptyArray:使用bytes.Compare解决方案,预分配固定大小的空字节数组。
  • CompareAllBytesWithDynamicEmptyArray:使用bytes.Compare解决方案,无需预先分配固定大小的空字节数组。

结果

BenchmarkIsAllBytesZero10-8                                 254072224            4.68 ns/op
BenchmarkIsAllBytesZero100-8                                132266841            9.09 ns/op
BenchmarkIsAllBytesZero1000-8                               19989015            55.6 ns/op
BenchmarkIsAllBytesZero10000-8                               2344436           507 ns/op
BenchmarkIsAllBytesZero100000-8                              1727826           679 ns/op
BenchmarkNaiveCheckAllBytesAreZero10-8                      234153582            5.15 ns/op
BenchmarkNaiveCheckAllBytesAreZero100-8                     30038720            38.2 ns/op
BenchmarkNaiveCheckAllBytesAreZero1000-8                     4300405           291 ns/op
BenchmarkNaiveCheckAllBytesAreZero10000-8                     407547          2666 ns/op
BenchmarkNaiveCheckAllBytesAreZero100000-8                     43382         27265 ns/op
BenchmarkCompareAllBytesWithFixedEmptyArray10-8             415171356            2.71 ns/op
BenchmarkCompareAllBytesWithFixedEmptyArray100-8            218871330            5.51 ns/op
BenchmarkCompareAllBytesWithFixedEmptyArray1000-8           56569351            21.0 ns/op
BenchmarkCompareAllBytesWithFixedEmptyArray10000-8           6592575           177 ns/op
BenchmarkCompareAllBytesWithFixedEmptyArray100000-8           567784          2104 ns/op
BenchmarkCompareAllBytesWithDynamicEmptyArray10-8           64215448            19.8 ns/op
BenchmarkCompareAllBytesWithDynamicEmptyArray100-8          32875428            35.4 ns/op
BenchmarkCompareAllBytesWithDynamicEmptyArray1000-8          8580890           140 ns/op
BenchmarkCompareAllBytesWithDynamicEmptyArray10000-8         1277070           938 ns/op
BenchmarkCompareAllBytesWithDynamicEmptyArray100000-8         121256         10355 ns/op

摘要

假设我们谈论的是稀疏零字节数组中的条件。根据基准测试,如果性能是一个问题,那么天真的检查解决方案将是一个坏主意。而且,如果你不想在你的项目中使用unsafe包,那么可以考虑使用预分配空数组的bytes.Compare解决方案作为替代方案。
可以指出的一个有趣的点是,来自unsafe包的性能变化很大,但它基本上优于上述所有其他解决方案。我认为这与CPU缓存机制有关。

j1dl9f46

j1dl9f463#

你可以使用bytes.equal或bytes.contains来与初始化为零的字节片进行比较,参见https://play.golang.org/p/mvUXaTwKjP,我还没有检查性能,但希望它已经被优化了。如果需要,您可能希望尝试其他解决方案并比较性能数字。

tpgth1q7

tpgth1q74#

我认为如果在循环内部使用binary or而不是if condition会更好(更快):

func isZero(bytes []byte) bool {
    b := byte(0)
    for _, s := range bytes {
        b |= s
    }
    return b == 0
}

字符串
通过使用前面的答案中提到的uint64的想法,可以进一步优化这一点

相关问题