You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何Golang的memmove(copy/append)性能差异如此显著?

性能问题求助:两段Go代码的memmove性能差异分析

我近期遇到一个难以解释的性能问题,希望能理解两段代码性能差异巨大的原因。

我正尝试重构一个库的代码以避免性能退化,目标是实现一个内存io.Writer,支持持续追加数据且无需像bytes.Buffer那样执行昂贵的重分配与复制操作。我的解决方案是使用字节切片的切片([][]byte),写入时分配新的字节切片——外层切片可能需要重分配,但相较于数据规模它很小,且实际数据写入后无需再复制。

基础结构体定义如下:

type noReallocWriter struct {
    data [][]byte
    idx  int
    off  int
}

第一个实现:固定容量切片版本

所有独立字节切片都以固定容量分配,Write时向其中复制数据直到满,再分配新的切片。我原以为这会受益于大块连续数据缓冲区及较低的分配频率,但实际写入场景中性能反而更差。

func (w *noReallocWriter) Write(p []byte) (int, error) {
    lenP := len(p)
    if lenP == 0 {
        return 0, nil
    }
    for len(p) > 0 {
        if w.idx == len(w.data) {
            w.data = append(w.data, make([]byte, 0, FIXED_SIZE))
        }
        curData := w.data[w.idx]
        n := copy(curData[w.off:cap(curData)], p)
        w.data[w.idx] = curData[:w.off+n]
        w.off += n
        p = p[n:]
        if w.off >= cap(curData) {
            w.idx++
            w.off = 0
        }
    }
    return lenP, nil
}

第二个实现:可变容量切片版本

为排查问题,我编写了另一个版本,更贴近旧版代码的行为:每次Write都分配一个与输入数据长度匹配容量的新字节切片,并将数据复制进去。

func (w *noReallocWriter) Write(p []byte) (n int, err error) {
    if w.idx != len(w.data) {
        panic("noReallocWriter only supports appending to the end.")
    }

    if len(p) == 0 {
        return 0, nil
    }

    buf := make([]byte, 0, len(p))
    buf = append(buf, p...)
    w.data = append(w.data, buf)
    w.idx++
    return len(p), nil
}

测试结果与疑问

  • 在仅测试写入大量数据的极简合成测试中,两者性能相近;但在应用场景的合成测试中,第二个版本性能显著更优,固定容量版本的memmove耗时是后者的3-4倍。
  • 两种实现的耗时基本都在memmove(copy/append)操作上,但固定容量切片版本的耗时分布在memmove_amd64.s的372-375行,而可变容量缓冲区版本的耗时全部集中在375行。
  • 测试模式:两个版本写入相同数据与模式,写入大小在小(35字节)和大(略小于256KB,可变)之间交替。测试了32KB到1MB间不同的FIXED_SIZE值,64KB到256KB区间性能相近,超出该范围性能下降。

我疑惑的是:为何复制相同数据、块大小相近的情况下,memmove的执行分布与耗时差异如此大?

内容的提问来源于stack exchange,提问作者Zach

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 04:08:16