为何读取多文件数据远慢于单文件?R语言读取优化求助
R中多CSV文件读取性能优化问题
问题背景
我需要频繁在R中读取一个大型数据集:约70万行、129列,数据被拆分成258个格式一致、大小10-24000KB的独立文件。测试发现合并为单个.csv文件后读取速度远快于多文件:
- 使用
readr时,多文件读取耗时是单文件的5倍以上 - 改用
fread后,多文件读取仍显著慢于单文件
由于无法改变文件拆分状态,且Windows下用CMD copy合并文件速度也慢,特询问:
- 多文件读取更慢的原因?
- 有无方法让多文件读取速度接近单文件?是否与磁盘硬件特性有关?
测试代码及结果
readr测试代码与结果
library(readr) library(microbenchmark) multiple = function() { #按原始方式读取258个文件 lapply(multiple_filepaths, function(x) read_csv(x, show_col_types = FALSE, progress = FALSE, col_types = coltypes)) } single = function() { #读取合并后的单个文件 read_csv(single_filepath, show_col_types = FALSE, progress = FALSE, col_types = coltypes) } microbenchmark(multiple(), single(), times = 10)
测试结果:
Unit: seconds expr min lq mean median uq max neval multiple() 40.811196 40.988007 41.330675 41.279320 41.665536 41.948959 10 single() 5.258331 5.401542 6.016591 5.749491 6.668737 6.947849 10
fread测试结果
Unit: seconds expr min lq mean median uq max neval multiple() 40.972344 41.136988 41.319579 41.301631 41.493197 41.684763 3 single() 6.224543 6.487988 6.993778 6.751433 7.378395 8.005358 3 multiplefread() 19.16834 19.23708 19.33322 19.30583 19.41566 19.52543 3 singlefread() 2.195246 3.631488 5.053056 5.067730 6.481960 7.896188 3
问题解答
1. 多文件读取更慢的核心原因
- 磁盘IO开销:每个文件读取都需要触发一次磁盘打开、定位、读取、关闭流程,258个文件就会产生258次这类操作,而单文件仅需一次。磁盘寻道时间(机械盘尤为突出)和文件系统元数据查询的开销会被大幅放大。
- 重复初始化开销:每次读取小文件时,解析器都要重复初始化列类型检测(即便指定了
col_types仍有少量开销)、小批量内存分配等操作,累积起来远大于一次性处理大文件的开销。 - 系统调用成本:每次文件读取都会涉及操作系统的系统调用,多次调用的上下文切换成本不可忽视。
2. 优化多文件读取速度的方法
(1)并行化读取
利用R的并行计算包,将文件读取任务分配到多个CPU核心,抵消单线程读取的等待时间。示例代码:
library(data.table) library(parallel) # 创建并行集群(保留1个核心给系统) cl <- makeCluster(detectCores() - 1) # 导出必要变量到集群环境 clusterExport(cl, c("multiple_filepaths", "coltypes")) # 并行读取文件 file_list <- parLapply(cl, multiple_filepaths, function(x) { fread(x, colClasses = coltypes) }) # 停止集群 stopCluster(cl) # 合并数据 combined_data <- rbindlist(file_list)
注意:并行化对机械硬盘提升有限(磁盘IO是瓶颈),但对SSD会有明显效果。
(2)预合并为二进制格式(离线处理)
虽然无法改变原始文件拆分状态,但可以在第一次读取后将合并的数据保存为二进制格式(如.rds、.fst或.parquet),后续直接读取该二进制文件即可:
# 第一次读取并保存 library(data.table) library(fst) data_list <- lapply(multiple_filepaths, fread, colClasses = coltypes) combined_data <- rbindlist(data_list) write_fst(combined_data, "combined_data.fst") # 后续直接读取 combined_data <- read_fst("combined_data.fst")
二进制格式读取速度远快于CSV,且只需一次合并操作,后续可反复复用。
(3)优化磁盘硬件
- 替换为SSD:SSD的随机读取性能远高于机械盘,能大幅降低多文件读取的寻道时间开销,这也是你当前多文件读取耗时高的核心因素之一。
- 优化文件系统:Windows下可开启NTFS压缩(CSV这类文本文件压缩率高,读取时解压开销远小于IO节省),或使用更高效的文件系统。
(4)减少读取额外开销
- 确保
col_types/colClasses参数完全准确,避免解析器做类型推断; - 关闭进度条、类型提示等额外输出;
- 优先使用
data.table::fread,从测试结果看,它的多文件读取速度已是readr::read_csv的2倍左右,底层实现更高效。
内容的提问来源于stack exchange,提问作者Richard Berry
相关产品推荐
相关产品推荐

