Python复制大文件速度慢

23

我正在尝试使用 shutil.copy 将一个大于1GB的文件从硬盘复制到USB驱动器。下面是描述我正在尝试做的事情的简单脚本:

import shutil
src_file = "source\to\large\file"
dest = "destination\directory"
shutil.copy(src_file, dest)

在 Linux 上只需要 2-3 分钟。但是在 Windows 下,相同的文件复制需要超过 10-15 分钟。有人可以解释一下为什么,并提供一些解决方案吗?最好使用 Python 代码。

更新 1

将文件保存为 test.py。源文件大小为 1 GB。目标目录位于 USB 驱动器中。使用 ptime 计算文件复制时间。结果如下:

ptime.exe test.py

ptime 1.0 for Win32, Freeware - http://www.
Copyright(C) 2002, Jem Berkes <jberkes@pc-t

===  test.py ===

Execution time: 542.479 s

542.479秒等于9分钟。我认为 shutil.copy 拷贝1GB文件不应该需要9分钟。

更新2

USB的健康状况良好,因为同样的脚本在Linux下运行良好。使用相同的文件在Windows本地xcopy中计算时间。以下是结果。

ptime 1.0 for Win32, Freeware - http://www.pc-tools.net/
Copyright(C) 2002, Jem Berkes <jberkes@pc-tools.net>

===  xcopy F:\test.iso L:\usb\test.iso
1 File(s) copied

Execution time: 128.144 s

128.144秒等于2.13分钟。拷贝测试文件后我还有1.7GB的剩余空间。


由于有这么多信息,我恐怕必须回答你的问题是否定的! - hivert
这个问题非常简单,脚本也很简单,只是将一个文件从源复制到目标。添加了几行代码,看起来像Python脚本。我不明白为什么会被投票否决 :-( - sundar_ima
我怀疑这里的问题部分原因是Windows将USB磁盘识别为便携设备,并优化了它以实现快速移除(代价是缓存)。例如,请参见https://www.thewindowsclub.com/enable-disable-disk-write-caching-windows-7-8。 - Cameron Kerr
Python 3.8 包含了 shutil 的重大改进/加速。请参见下面的链接。 - fantabolous
2个回答

34

补充一些有趣的信息:Windows 不喜欢 shutil 实现内部使用的微小缓冲区。

我快速尝试了以下操作:

  • 将原始的shutil.py文件复制到示例脚本文件夹中并将其重命名为myshutil.py
  • 将第一行更改为import myshutil
  • 编辑myshutil.py文件并将copyfileobj

def copyfileobj(fsrc,fdst,length = 16 * 1024):

更改为

def copyfileobj(fsrc,fdst,length = 16 * 1024 * 1024):

使用16 MB缓冲区而不是16 KB可以大大提高性能。

也许Python需要一些定向于Windows内部文件系统特征的调整?

编辑:

在文件开头添加以下内容会更好:

import shutil

def _copyfileobj_patched(fsrc, fdst, length=16*1024*1024):
    """Patches shutil method to hugely improve copy speed"""
    while 1:
        buf = fsrc.read(length)
        if not buf:
            break
        fdst.write(buf)
shutil.copyfileobj = _copyfileobj_patched

这是对当前实现的简单补丁,在这里完美无缝运行。

Python 3.8+: Python 3.8包括了shutil的重大改进,包括将Windows缓冲区从16KB增加到1MB(仍然低于这个问题中建议的16MB)。请参见ticketdiff


2
在网上找到了这个:http://blogs.blumetech.com/blumetechs-tech-blog/2011/05/faster-python-file-copy.html - mgruber4
2
看 https://github.com/python/cpython/blob/master/Lib/shutil.py 这里,它们似乎已经更改了 copyfileobj 的实现方式,取决于操作系统使用一个名为 length 的值。当在 Windows 上运行时,现在会使用 1MB 缓冲区(而不是 16K)。 - Chau
1
由于Python是动态的,解决方案的想法是通过替换原始函数为一个修补版本,定义一个新的默认缓冲区大小。打开库源代码(Lib/shutil.py)。在那里,你会看到_copyfileobj_patched几乎是从原始函数克隆而来,除了“default”参数之外。 编辑:您还应该注意shutil.copyfile消耗shutil.copyfileobj... - mgruber4
@Chau 看起来Python 3.8中的1MB缓冲区更改是新的:https://bugs.python.org/issue33671 - fantabolous
在shutil中,我们可以有自定义的缓冲区长度吗? - Farhan Hai Khan
显示剩余2条评论

6
你的问题与Python无关。实际上,与Linux系统相比,Windows的复制过程真的很差。
根据这篇文章,你可以使用xcopyrobocopy来改善此问题:Is (Ubuntu) Linux file copying algorithm better than Windows 7?。但在这种情况下,你需要为Linux和Windows分别调用不同的命令...
import os
import shutil
import sys

source = "source\to\large\file"
target = "destination\directory"

if sys.platform == 'win32':
    os.system('xcopy "%s" "%s"' % (source, target))
else:
    shutil.copy(source, target)

参见:


我必须通过调用系统命令subprocess.call(["xcopy",source ,target], shell=True)来解决问题。但是,shutil.copy()肯定存在问题。 - sundar_ima
这个答案是可行的,但下面的答案提供了问题的解释,不需要特定于系统的代码,并且符合Pythonic风格。 - DonkeyKong

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接