Golang中io.Copy与内存读取的性能对比

Golang中io.Copy与内存读取的性能对比 我想了解将文件读入内存与使用 io.Copy 写入目标之间有什么区别。这里有一个例子可以更清楚地说明:

  1. 将文件读入内存
	file, err := os.Open(path)
  if err != nil { return }
	fileContents, err := ioutil.ReadAll(file)
  if err != nil { return }
	fi, err := file.Stat()
  if err != nil { return }
  file.Close()

	body := &bytes.Buffer{}
	writer := multipart.NewWriter(body)
	
		part, err := writer.CreateFormFile(paramName, fi.Name())
    if err != nil { return }
		part.Write(fileContents)

	writer.Close()
  1. 使用 io.Copy
file, err := os.Open(path)
	if err != nil {
		return
	}
	defer file.Close()

	fi, err := file.Stat()
	if err != nil {
		return 
	}

	body := &bytes.Buffer{}
	writer := multipart.NewWriter(body)
	part, err := writer.CreateFormFile(paramName, fi.Name())
	if err != nil {
        return
	}
	io.Copy(part, file)

我有点觉得,即使复制方法是直接写入到 part writer,这个变量仍然是在内存中 🤷 所以我看不出区别。🤔 🤔


更多关于Golang中io.Copy与内存读取的性能对比的实战教程也可以访问 https://www.itying.com/category-94-b0.html

2 回复

更多关于Golang中io.Copy与内存读取的性能对比的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


io.Copy 与完全读入内存的主要区别在于内存使用模式和性能特征。以下是关键对比:

内存使用差异

完全读入内存方案:

fileContents, err := ioutil.ReadAll(file)  // 一次性分配整个文件大小的内存
// 此时内存中同时存在:
// 1. fileContents 的完整副本
// 2. body.Buffer 中的另一个完整副本(通过 part.Write)

内存峰值 ≈ 文件大小 × 2

io.Copy 方案:

io.Copy(part, file)  // 使用固定大小的缓冲区进行流式复制

内存峰值 ≈ 缓冲区大小(默认32KB)

性能基准测试示例

package main

import (
    "bytes"
    "io"
    "mime/multipart"
    "os"
    "testing"
)

func BenchmarkReadAll(b *testing.B) {
    for i := 0; i < b.N; i++ {
        file, _ := os.Open("test.dat")
        content, _ := io.ReadAll(file)
        file.Close()
        
        body := &bytes.Buffer{}
        writer := multipart.NewWriter(body)
        part, _ := writer.CreateFormFile("file", "test.dat")
        part.Write(content)
        writer.Close()
    }
}

func BenchmarkIOCopy(b *testing.B) {
    for i := 0; i < b.N; i++ {
        file, _ := os.Open("test.dat")
        defer file.Close()
        
        body := &bytes.Buffer{}
        writer := multipart.NewWriter(body)
        part, _ := writer.CreateFormFile("file", "test.dat")
        io.Copy(part, file)
        writer.Close()
    }
}

关键区别

  1. 内存效率io.Copy 使用固定缓冲区(默认32KB)流式传输数据,避免一次性加载大文件
  2. 大文件处理:对于超过可用内存的文件,ioutil.ReadAll 会直接失败(panic: runtime error)
  3. GC压力ReadAll 创建的大对象给垃圾回收带来更大压力

实际影响示例

处理1GB文件时:

// 方案1:可能因内存不足而崩溃
content, _ := io.ReadAll(file)  // 尝试分配1GB连续内存

// 方案2:稳定运行
io.Copy(part, file)  // 仅使用32KB缓冲区

特殊情况

当需要多次访问数据时,完全读入内存可能更合适:

// 需要多次处理同一数据
content, _ := io.ReadAll(file)
process1(content)
process2(content)  // 避免重复I/O

但在你的multipart表单示例中,数据只写入一次,io.Copy 是更优选择。它减少了内存分配次数,避免了不必要的完整拷贝,特别是在处理大文件时优势明显。

回到顶部