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

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

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

谢谢!

卢卡斯。

问题:

  1. 为什么复制10 GiB文件与1 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. 有没有替代,更好的performance方式复制文件,但也获得进度更新?

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 

testing环境:

 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 

问题3:

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

问题4:

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

问题5:

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

很有可能,因为你以不同的方式测量完成时间。

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

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

如果你想要一个有意义的测量,

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

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

那么我希望你会看到更一致的时间。

回答你的问题2:

shutil.copyfile()非常慢,因为默认情况下它使用一个16K字节的复制缓冲区。 最终它会以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的缓冲区,则会显着提高性能。