高效内存方式实现Golang []byte追加操作

高效内存方式实现Golang []byte追加操作 我有一种感觉,dump = append([]byte(fmt.Sprintf( ... 这行代码在内存使用上不够高效,因为我不知道字节的大小,我使用了 fmt 并且将字符串转换为字节。

请问如何改进这一点?

谢谢

	dump, err := httputil.DumpResponse(res, true)
	if err != nil {
		return err
	}

	met := http.MethodGet
	if res.request.Method != "" {
		met = res.request.Method
	}

	uri := res.request.RequestURI
	if uri == "" {
		uri = res.request.URL.RequestURI()
	}

	dump = append([]byte(fmt.Sprintf("%s %s HTTP/%d.%d\nHost: %s\n",
		met,
		uri,
		res.request.ProtoMajor,
		res.request.ProtoMinor,
		res.request.Host,
	)), dump...)

更多关于高效内存方式实现Golang []byte追加操作的实战教程也可以访问 https://www.itying.com/category-94-b0.html

2 回复

事实上,你唯一不知道长度的变量是 urihost,这两者通常都小于 256。 因此,你可以直接申请一个大小为 512+len(dump) 的缓冲区来解决大部分问题。 但这带来了一个新问题。如果你什么都不做,append 只有在遇到扩容时才会申请一次内存,这与预先申请内存的方式差别不大。

  1. 你可能担心 fmt 的效率,但它是一个格式化工具,可以帮助你格式化输出。如果你不追求极致的效率,优化 fmt 并不是一个好主意。
  2. 如果你担心 stringbyte 的转换成本,可以使用 unsafe.Pointer 进行转换。同样地,如果你不追求极致的效率,不建议用如此繁琐的方式去做。 总而言之,如果你后续不修改字节切片,当前的方法是一个合理的实现。

更多关于高效内存方式实现Golang []byte追加操作的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


要高效地实现 []byte 追加操作,避免不必要的内存分配和类型转换,可以使用 bytes.Buffer 或直接使用 append 配合 strconv 等低开销函数。以下是改进后的代码示例:

import (
    "bytes"
    "net/http/httputil"
    "strconv"
)

func dumpResponse(res *http.Response) error {
    dump, err := httputil.DumpResponse(res, true)
    if err != nil {
        return err
    }

    met := http.MethodGet
    if res.Request.Method != "" {
        met = res.Request.Method
    }

    uri := res.Request.RequestURI
    if uri == "" {
        uri = res.Request.URL.RequestURI()
    }

    var buf bytes.Buffer
    buf.WriteString(met)
    buf.WriteByte(' ')
    buf.WriteString(uri)
    buf.WriteString(" HTTP/")
    buf.WriteString(strconv.Itoa(res.Request.ProtoMajor))
    buf.WriteByte('.')
    buf.WriteString(strconv.Itoa(res.Request.ProtoMinor))
    buf.WriteString("\nHost: ")
    buf.WriteString(res.Request.Host)
    buf.WriteByte('\n')
    
    // 将头部和原始dump合并
    headerBytes := buf.Bytes()
    result := make([]byte, len(headerBytes) + len(dump))
    copy(result, headerBytes)
    copy(result[len(headerBytes):], dump)
    
    // 使用result继续后续操作
    _ = result // 替换为实际使用
    return nil
}

或者使用更直接的 append 方式,避免中间字符串转换:

func dumpResponse(res *http.Response) error {
    dump, err := httputil.DumpResponse(res, true)
    if err != nil {
        return err
    }

    met := http.MethodGet
    if res.Request.Method != "" {
        met = res.Request.Method
    }

    uri := res.Request.RequestURI
    if uri == "" {
        uri = res.Request.URL.RequestURI()
    }

    // 预分配足够空间
    result := make([]byte, 0, len(met)+len(uri)+50+len(dump))
    
    result = append(result, met...)
    result = append(result, ' ')
    result = append(result, uri...)
    result = append(result, " HTTP/"...)
    result = append(result, strconv.Itoa(res.Request.ProtoMajor)...)
    result = append(result, '.')
    result = append(result, strconv.Itoa(res.Request.ProtoMinor)...)
    result = append(result, "\nHost: "...)
    result = append(result, res.Request.Host...)
    result = append(result, '\n')
    result = append(result, dump...)
    
    // 使用result继续后续操作
    _ = result // 替换为实际使用
    return nil
}

这两种方式都避免了 fmt.Sprintf 产生的临时字符串和 []byte() 转换带来的额外内存分配,通过直接操作字节切片或使用缓冲区来提高内存效率。

回到顶部