在 Go 中生成长随机字符串的最快方法是什么?

2024-04-14

像 [a-zA-Z0-9] 字符串:

na1dopW129T0anN28udaZ

或十六进制字符串:

8c6f78ac23b4a7b8c0182d

我所说的“长”是指 2K 及更多字符。


我的盒子上的速度约为 200MBps。显然还有改进的空间。

type randomDataMaker struct {
    src rand.Source
}

func (r *randomDataMaker) Read(p []byte) (n int, err error) {
    for i := range p {
        p[i] = byte(r.src.Int63() & 0xff)
    }
    return len(p), nil
}

你只需使用io.CopyN产生你想要的字符串。显然你可以在进入时调整字符集或其他什么。

这个模型的好处是它只是一个io.Reader所以你可以用它来做任何东西。

测试如下:

func BenchmarkRandomDataMaker(b *testing.B) {
    randomSrc := randomDataMaker{rand.NewSource(1028890720402726901)}
    for i := 0; i < b.N; i++ {
        b.SetBytes(int64(i))
        _, err := io.CopyN(ioutil.Discard, &randomSrc, int64(i))
        if err != nil {
            b.Fatalf("Error copying at %v: %v", i, err)
        }
    }
}

在我的 2.2GHz i7 的一个核心上:

BenchmarkRandomDataMaker       50000        246512 ns/op     202.83 MB/s

EDIT

自从我编写基准测试以来,我认为我应该做明显的改进(减少随机调用的频率)。由于对 rand 的调用减少了 1/8,它的运行速度大约提高了 4 倍,尽管它更加丑陋:

新版本:

func (r *randomDataMaker) Read(p []byte) (n int, err error) {
    todo := len(p)
    offset := 0
    for {
        val := int64(r.src.Int63())
        for i := 0; i < 8; i++ {
            p[offset] = byte(val & 0xff)
            todo--
            if todo == 0 {
                return len(p), nil
            }
            offset++
            val >>= 8
        }
    }

    panic("unreachable")
}

新基准:

BenchmarkRandomDataMaker      200000        251148 ns/op     796.34 MB/s

EDIT 2

删除了转换为字节中的掩码,因为它是多余的。更快地获得优惠:

BenchmarkRandomDataMaker      200000        231843 ns/op     862.64 MB/s

(这比实际工作容易多了sigh)

EDIT 3

今天 irc 中出现了这个问题,所以我发布了一个库。此外,我的实际基准测试工具虽然对于相对速度很有用,但其报告不够准确。

我创建randbo https://github.com/dustin/randbo您可以在任何需要的地方重复使用它们来生成随机流。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在 Go 中生成长随机字符串的最快方法是什么? 的相关文章

随机推荐