Оценка Python x64 в Windows x64: оценка производительности файла / проблема - PullRequest
5 голосов
/ 25 ноября 2010

при программировании приложения для резервного копирования я провел оценку производительности копирования файлов в Windows.

У меня есть несколько вопросов, и мне интересно ваше мнение.

Спасибо!

Лукас.

Вопросы:

  1. Почему при копировании файла 10 ГиБ производительность намного ниже, чем при 1 ГиБфайл?

  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. Существует ли альтернативный, более эффективный способ копирования файлов, а также получения обновлений прогресса?

Результаты для1 ГиБ и файл 10 ГиБ:

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 ]

3 голосов
/ 08 декабря 2014

Вопрос 3:

Вы неправильно интерпретируете флаг COPY_FILE_NO_BUFFERING в API Microsoft. Это не int 1000, а hex 1000 (0x1000 => int значение: 4096). Когда вы установите CopyFlags = 4096, у вас будет (?) Самая быстрая процедура копирования в среде Windows. Я использую ту же самую процедуру в своем коде резервного копирования данных, которая очень быстра и передает данные размером в терабайт изо дня в день.

Вопрос 4:

Это не имеет значения, поскольку это обратный вызов. Но в целом вы не должны помещать слишком много кода внутрь и содержать его в чистоте и гладкости.

Вопрос 5:

По моему опыту, это самая быстрая процедура копирования в стандартной среде Windows. Там могут быть более быстрые пользовательские процедуры копирования, но при использовании простого Windows API ничего лучше не найти.

3 голосов
/ 25 ноября 2010

По всей вероятности, потому что вы измеряете время завершения по-разному.

Я предполагаю, что файл объемом 1 Гб удобно помещается в оперативную память.Поэтому ОС, вероятно, просто кэширует его и сообщает вашему приложению, что оно скопировано, когда большая его часть (возможно, вся) еще не очищена в буферах ядра.

Однако файл 10G не помещается в ram, поэтомудолжен написать (большую часть) его, прежде чем он скажет, что он закончен.

Если вы хотите значимого измерения,

a) Очистите буферный кеш файловой системы перед каждым запуском - если ваша ОС не предоставляетУдобный способ сделать это, перезагрузиться (примечание: Windows не предоставляет удобный метод, я думаю, что есть инструмент для внутренних систем, который делает это, хотя).В случае сетевой файловой системы очистите кеш на сервере тоже.

b) Синхронизируйте файл на диске после того, как вы закончите, прежде чем измерять время завершения

Тогда я ожидаю, что выУвидим более последовательные времена.

2 голосов
/ 05 апреля 2013

Чтобы ответить на ваш вопрос 2.:

shutil.copyfile () очень медленный, потому что по умолчанию он использует буфер копирования 16 Кбайт.В конце концов он заканчивается в 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.Если бы вы использовали copyfileobj () непосредственно в вашем файле GB, но, например, с буфером 128 МБ, вы бы увидели резкое улучшение производительности.

1 голос
/ 31 октября 2018

Лукас, я считаю, что следующий способ работает на 20% быстрее, чем win32file.CopyFile.

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)
...