【发布时间】:2020-11-17 19:29:24
【问题描述】:
对于一个项目,我想为大约 5000 万行 CSV 中的每一行手动创建结构。为此,我逐行遍历文件并将每个结构附加到切片。这是简化的方法:
func readCSV(filePath string) DataFrame {
file, _ := os.Open(filePath)
defer file.Close()
var rows []Row
scanner := bufio.NewScanner(file)
scanner.Scan()
for scanner.Scan() {
parts := strings.Split(scanner.Text(), ",")
if len(parts) < 7 {
continue
}
column1, _ := strconv.Atoi(parts[0])
column2, _ := strconv.ParseFloat(parts[1], 32)
column3, _ := strconv.ParseFloat(parts[2], 32)
column4 := parts[3]
column5, _ := strconv.ParseFloat(parts[4], 32)
column6 := parts[5]
column7 := parts[6]
row := Row{
Column1: column1,
Column2: column2,
Column3: column3,
Column4: column4,
Column5: column5,
Column6: column6,
Column7: column7,
}
rows = append(rows, row)
}
return DataFrame{
Rows: rows,
}
}
生成的 DataFrame 有大约 3 GB 的内存。问题是在方法执行期间 RAM 消耗达到顶峰,而 Go 进程使用 15GB 以上的内存,使该函数无法用于我的目的。切片返回后,进程的 RAM 消耗将降至预期的 3GB。
堆配置文件如下所示:
3.26GB 5.81GB (flat, cum) 100% of Total
. . 62: scanner := bufio.NewScanner(file)
. . 63: scanner.Scan()
. . 64: for scanner.Scan() {
. 2.55GB 65: parts := strings.Split(scanner.Text(), ",")
. . 66: if len(parts) < 7 {
. . 67: continue
. . 68: }
. . 69: column1, _ := strconv.Atoi(parts[0])
. . 70: column2, _ := strconv.ParseFloat(parts[1], 32)
. . 71: column3, _ := strconv.ParseFloat(parts[2], 32)
. . 72: column4 := parts[3]
. . 73: column5, _ := strconv.ParseFloat(parts[4], 32)
. . 74: column6 := parts[5]
. . 75: column7 := parts[6]
. . 76: row := Row{
. . 77: Column1: column1,
. . 78: Column2: column2,
. . 79: Column3: column3,
. . 80: Column4: column4,
. . 81: Column5: column5,
. . 82: Column6: column6,
. . 83: Column7: column7,
. . 84: }
3.26GB 3.26GB 85: rows = append(rows, row)
. . 86: }
. . 87:
. . 88: return DataFrame{
. . 89: Rows: rows,
我不知道高 RAM 消耗从何而来。我试图手动调用垃圾收集器但没有成功。谁能给个提示?
【问题讨论】:
-
使用文件大小来估计行数。使用
rows := make([]Row, 0, estimate)预分配大小。 -
+1 尽可能预分配切片。每次分配都会产生一个新的后备数组,旧数组将被 GC 释放,最终可能被操作系统回收。从足够大的切片开始,您可以避免这种情况。
-
@Konstantin 在
rows = append(rows, row)分配的内存应该会显着下降,并且可以很好地估计总行数。你是说没有? -
你考虑过流处理吗?
-
您也可以尝试使用 csv 阅读器而不是字符串拆分。 golang.org/pkg/encoding/csv/#Reader 可能会有帮助。
标签: go memory memory-management ram