如何在Python中快速迁移Ubuntu下跨驱动器的数百GB大文件并显示进度
1. 和shutil.move的速度差异
分两种场景对比:
- 同文件系统内移动:
shutil.move直接修改文件系统元数据,不需要拷贝数据,瞬间完成,速度远快于你自己实现的读写拷贝方案,差异极大。 - 跨驱动器移动:
shutil.move底层会调用shutil.copyfile完成数据拷贝,再删除原文件,和你的实现逻辑本质一致。但你当前代码里chunk_size=4096的设置太小,会导致循环开销远高于shutil.copyfile(其默认块大小为1MB),实际运行速度会比shutil.move慢30%以上,差异非常明显。如果将块大小调整到1MB~32MB区间后,加上tqdm的开销可以忽略,速度和shutil.move基本持平。
2. 更高效的实现方案
核心优化方向围绕减少拷贝开销、降低循环次数:
- 调整块大小:磁盘IO的最小单位通常是4KB,但连续读写时用更大的块可以大幅减少用户态循环开销,推荐设置为`16 * 1024 * 1024(16MB),内存占用极低,速度提升明显。
- 使用Linux零拷贝系统调用
os.sendfile:普通的read/write需要将数据从内核缓冲区拷贝到用户态再拷回内核缓冲区,sendfile直接在内核态完成数据拷贝,不需要经过用户态,CPU占用降低50%以上,速度也会有10%~20%的提升,示例代码如下:
import os from pathlib import Path from tqdm import tqdm large_file = Path('/path/to/large_file') new_loc = Path('/new/path/large_file') chunk_size = 16 * 1024 * 1024 # 16MB 块大小 file_size = large_file.stat().st_size with open(large_file, 'rb') as f_in, open(new_loc, 'wb') as f_out: with tqdm(total=file_size, unit='B', unit_scale=True, desc="传输进度") as pbar: offset = 0 while offset < file_size: sent = os.sendfile(f_out.fileno(), f_in.fileno(), offset, chunk_size) if sent == 0: raise RuntimeError("文件读取提前结束") offset += sent pbar.update(sent) # 校验文件完整性后再删除原文件 if new_loc.stat().st_size == file_size: # 可选:补充哈希校验,避免数据传输损坏 large_file.unlink() else: new_loc.unlink() raise RuntimeError("文件传输不完整,已删除损坏的目标文件")
- 增加完整性校验:数百GB的文件传输过程中存在数据损坏的风险不低,建议传输完成后可以对比文件哈希值再删除原文件,避免数据丢失。
3. 是否需要用C扩展或ctypes
完全没有必要。跨驱动器大文件传输的瓶颈完全在磁盘IO速度上,Python层的逻辑开销占总耗时的比例不到1%,就算用纯C实现相同逻辑,最终速度也受限于磁盘的连续读写速度,不会有可感知的提升。当前场景下使用os.sendfile已经直接调用了系统底层的最优接口,不需要额外做底层优化。
内容的提问来源于stack exchange,提问作者Andres
相关产品推荐
相关产品推荐

