【问题标题】:Efficient way to convert string to string representation of the binary将字符串转换为二进制字符串表示的有效方法
【发布时间】:2023-04-02 18:08:01
【问题描述】:

我正在寻找如何以最佳性能将字符串转换为二进制字符串表示形式。

所以我从类似于以下内容开始:

func binConvertOrig(s string) string {
    var buf bytes.Buffer
    for i := 0; i < len(s); i++ {
        fmt.Fprintf(&buf, "%08b", s[i])
    }
    return buf.String()
}

s := "Test"
log.Printf("%s => binConvertOrig => %s", s, binConvertOrig(s))

但似乎fmt.Fprintf & bytes.Buffer 效率不高。

有没有更好的方法来做到这一点?

谢谢

【问题讨论】:

    标签: string performance go binary type-conversion


    【解决方案1】:

    没有什么比预先计算好的查找表更好的了,特别是如果它存储在一个切片或数组中(而不是在一个映射中),并且转换器为结果分配了一个大小合适的字节切片:

    var byteBinaries [256][]byte
    
    func init() {
        for i := range byteBinaries {
            byteBinaries[i] = []byte(fmt.Sprintf("%08b", i))
        }
    }
    
    func strToBin(s string) string {
        res := make([]byte, len(s)*8)
        for i := len(s) - 1; i >= 0; i-- {
            copy(res[i*8:], byteBinaries[s[i]])
        }
        return string(res)
    }
    

    测试它:

    fmt.Println(strToBin("\x01\xff"))
    

    输出(在Go Playground上试试):

    0000000111111111
    

    基准测试

    让我们看看它能达到多快:

    var texts = []string{
        "\x00",
        "123",
        "1234567890",
        "asdf;lkjasdf;lkjasdf;lkj108fhq098wf34",
    }
    
    func BenchmarkOrig(b *testing.B) {
        for n := 0; n < b.N; n++ {
            for _, t := range texts {
                binConvertOrig(t)
            }
        }
    }
    
    func BenchmarkLookup(b *testing.B) {
        for n := 0; n < b.N; n++ {
            for _, t := range texts {
                strToBin(t)
            }
        }
    }
    

    结果:

    BenchmarkOrig-4      200000     8526 ns/op       2040 B/op     12 allocs/op
    BenchmarkLookup-4   2000000      781 ns/op        880 B/op      8 allocs/op
    

    查找版本 (strToBin()) 快 11 倍,并且使用更少的内存和分配。基本上它只对结果使用分配(这是不可避免的)。

    【讨论】:

    • 非常感谢您的帮助。这很有启发性。
    • 很好的答案。尽管我不确定,range 可能比s 有一点点提升。您也可以尝试为目标切片中的索引定义一个j 变量并执行j += copy(res[j:], byteBinaries[s[i]])。不知道会不会有点效率?
    • @AlexanderTrakhimenok 在string 上的范围在runes 上,而不是在bytes 上!并且引入另一个您要编写的变量很可能会执行得更少。这个版本不需要写索引,因为它依赖于i,而i*8会被编译器优化为移位操作(例如i&lt;&lt;3)。
    • @icza,好点:)。无论如何,我想知道迭代的方向是否有任何区别。也许是大字符串的情况
    • @AlexanderTrakhimenok 是的,我也不确定。 len(s) 不是常量,因为 s 不是常量,因此它可能会或可能不会被缓存。我使用向下方向以防万一。
    猜你喜欢
    • 2012-10-26
    • 2011-08-03
    • 1970-01-01
    • 1970-01-01
    • 2020-12-31
    • 2014-08-02
    • 1970-01-01
    • 2015-04-23
    • 2019-08-23
    相关资源
    最近更新 更多