[英]Python x64 bit on Windows x64 copy file performance evaluation / problem
在编写一种备份应用程序时,我对Windows上的文件复制性能进行了评估。
我有几个问题,我想知道你的意见。
谢谢!
卢卡斯。
问题:
与1 GiB文件相比,复制10 GiB文件时为什么性能会慢得多?
为什么shutil.copyfile这么慢?
为什么win32file.CopyFileEx这么慢? 这可能是因为标志win32file.COPY_FILE_RESTARTABLE? 但是,它不接受int 1000作为标志(COPY_FILE_NO_BUFFERING),建议用于大型文件: http : //msdn.microsoft.com/en-us/library/aa363852%28VS.85%29.aspx
使用空的ProgressRoutine似乎对完全不使用ProgressRoutine没有影响。
是否有一种替代的,更好的复制文件的方式,但也获得进度更新?
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
您在Microsofts API中误解了COPY_FILE_NO_BUFFERING标志。 它不是int 1000而是hex 1000(0x1000 => int value:4096)。 设置CopyFlags = 4096时,您将在Windows环境中拥有(?)最快的复制例程。 我在数据备份代码中使用相同的例程,速度非常快,每天都会传输TB级的数据。
它无关紧要,因为它是一个回调。 但总的来说,你不应该在里面放太多的代码并保持它的清洁和光滑。
根据我的经验,它是标准Windows环境中最快的复制例程。 可能有更快的自定义复制例程,但是当使用普通的Windows API时,无法找到更好的。
很有可能,因为你以不同的方式衡量完成时间。
我猜测1Gb文件很适合ram。 因此,操作系统可能只是缓存它并告诉您的应用程序,当它的大部分(可能全部)仍未在内核缓冲区中刷新时,它会被复制。
但是,10G文件不适合ram,所以它必须在它完成之前写入(大部分)它。
如果你想要一个有意义的测量,
a)在每次运行之前清除文件系统缓冲区缓存 - 如果你的操作系统没有提供一种方便的方法,重新启动(注意:Windows没有提供方便的方法,我认为有一个系统内部工具可以做到这一点) 。 在网络文件系统的情况下,也清除服务器上的缓存。
b)在完成测量完成时间之前,将文件同步到光盘
然后我希望你会看到更多的一致时间。
回答你的问题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的缓冲区,你会看到性能大幅提升。
卢卡斯,我发现以下方式比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)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.