Windows x64上的Python x64位复制文件性能评估/问题

时间:2010-11-25 16:41:11

标签: python windows copy performance win32com

编写一种备份应用程序时,我在Windows上对文件复制性能进行了评估。

我有几个问题,我想知道你的意见。

谢谢!

卢卡斯。

问题:

  1. 与1 GiB文件相比,为什么复制10 GiB文件时性能会慢得多?

  2. 为什么shutil.copyfile这么慢?

  3. 为什么win32file.CopyFileEx这么慢? 这可能是因为标志win32file.COPY_FILE_RESTARTABLE? 但是,它不接受int 1000作为标志(COPY_FILE_NO_BUFFERING), 建议用于大文件: http://msdn.microsoft.com/en-us/library/aa363852%28VS.85%29.aspx

  4. 使用空的ProgressRoutine似乎对完全不使用ProgressRoutine没有影响。

  5. 是否有替代的,效果更好的方式来复制文件,还能获得进度更新?

  6. 1 GiB和10 GiB文件的结果:

    test_file_size             1082.1 MiB    10216.7 MiB
    
    METHOD                      SPEED           SPEED
    robocopy.exe                111.0 MiB/s     75.4 MiB/s
    cmd.exe /c copy              95.5 MiB/s     60.5 MiB/s
    shutil.copyfile              51.0 MiB/s     29.4 MiB/s
    win32api.CopyFile           104.8 MiB/s     74.2 MiB/s
    win32file.CopyFile          108.2 MiB/s     73.4 MiB/s
    win32file.CopyFileEx A       14.0 MiB/s     13.8 MiB/s
    win32file.CopyFileEx B       14.6 MiB/s     14.9 MiB/s
    

    测试环境:

    Python:
    ActivePython 2.7.0.2 (ActiveState Software Inc.) based on
    Python 2.7 (r27:82500, Aug 23 2010, 17:17:51) [MSC v.1500 64 bit (AMD64)] on win32
    
    source = mounted network drive
    source_os = Windows Server 2008 x64
    
    destination = local drive
    destination_os = Windows Server 2008 R2 x64
    

    备注:

    'robocopy.exe' and 'cmd.exe /c copy' were run using subprocess.call()
    

    win32file.CopyFileEx A(不使用ProgressRoutine):

    def Win32_CopyFileEx_NoProgress( ExistingFileName, NewFileName):
        win32file.CopyFileEx(
            ExistingFileName,                             # PyUNICODE           | File to be copied
            NewFileName,                                  # PyUNICODE           | Place to which it will be copied
            None,                                         # CopyProgressRoutine | A python function that receives progress updates, can be None
            Data = None,                                  # object              | An arbitrary object to be passed to the callback function
            Cancel = False,                               # boolean             | Pass True to cancel a restartable copy that was previously interrupted
            CopyFlags = win32file.COPY_FILE_RESTARTABLE,  # int                 | Combination of COPY_FILE_* flags
            Transaction = None                            # PyHANDLE            | Handle to a transaction as returned by win32transaction::CreateTransaction
            )
    

    win32file.CopyFileEx B(使用空ProgressRoutine):

    def Win32_CopyFileEx( ExistingFileName, NewFileName):
        win32file.CopyFileEx(
            ExistingFileName,                             # PyUNICODE           | File to be copied
            NewFileName,                                  # PyUNICODE           | Place to which it will be copied
            Win32_CopyFileEx_ProgressRoutine,             # CopyProgressRoutine | A python function that receives progress updates, can be None
            Data = None,                                  # object              | An arbitrary object to be passed to the callback function
            Cancel = False,                               # boolean             | Pass True to cancel a restartable copy that was previously interrupted
            CopyFlags = win32file.COPY_FILE_RESTARTABLE,  # int                 | Combination of COPY_FILE_* flags
            Transaction = None                            # PyHANDLE            | Handle to a transaction as returned by win32transaction::CreateTransaction
            )
    
    def Win32_CopyFileEx_ProgressRoutine(
        TotalFileSize,
        TotalBytesTransferred,
        StreamSize,
        StreamBytesTransferred,
        StreamNumber,
        CallbackReason,                         # CALLBACK_CHUNK_FINISHED or CALLBACK_STREAM_SWITCH
        SourceFile,
        DestinationFile,
        Data):                                  # Description
        return win32file.PROGRESS_CONTINUE      # return of any win32file.PROGRESS_* constant
    

4 个答案:

答案 0 :(得分:3)

很有可能,因为您以不同方式衡量完成时间。

我猜测1Gb文件很适合ram。因此,操作系统可能只是缓存它并告诉你的应用程序,当它的大部分(可能是全部)仍在内核缓冲区中未被清除时,它被复制。

但是,10G文件不适合ram,因此它必须在它完成之前写入(大部分)。

如果您想进行有意义的测量,

a)在每次运行之前清除文件系统缓冲区缓存 - 如果你的操作系统没有提供方便的方法,重新启动(注意:Windows不提供方便的方法,我认为有一个系统内部工具可以做这虽然)。在网络文件系统的情况下,也清除服务器上的缓存。

b)在完成测量完成时间之前,将文件同步到光盘

然后我希望你会看到更多的一致时间。

答案 1 :(得分:3)

问题3:

您在Microsofts API中误解了COPY_FILE_NO_BUFFERING标志。 它不是int 1000而是hex 1000(0x1000 => int value:4096)。设置CopyFlags = 4096时,您将在Windows环境中拥有(?)最快的复制例程。 我在数据备份代码中使用相同的例程,速度非常快,每天都会传输TB级数据。

问题4:

这并不重要,因为它是一个回调。但总的来说,你不应该在里面放太多代码并保持它的清洁和光滑。

问题5:

根据我的经验,它是标准Windows环境中最快的复制例程。可能有更快的自定义复制例程,但是当使用普通的Windows API时,无法找到更好的。

答案 2 :(得分:2)

回答你的问题2。:

shutil.copyfile()非常慢,因为默认情况下它使用16Kbyte的复制缓冲区。最终它最终出现在shutil.copyfileobj()中,如下所示:

def copyfileobj(fsrc, fdst, length=16*1024):
    """copy data from file-like object fsrc to file-like object fdst"""
    while 1:
        buf = fsrc.read(length)
        if not buf:
            break
        fdst.write(buf)

在你的情况下,它是在读取16K和写入16K之间的乒乓。如果您直接在GB文件上使用copyfileobj(),但是使用128MB的缓冲区,您会看到性能大幅提升。

答案 3 :(得分:1)

卢卡斯,我发现以下方法比win32file.CopyFile快20%。

b = bytearray(8 * 1024 * 1024) 
# I find 8-16MB is the best for me, you try to can increase it 
with io.open(f_src, "rb") as in_file:
    with io.open(f_dest, "wb") as out_file:
        while True:
            numread = in_file.readinto(b)
            if not numread:
                break
            out_file.write(b)
            # status bar update here
shutil.copymode(f_src, f_dest)