Python x64 bit på Windows x64 kopi fil ydeevaluering/problem

Indlæg af Hanne Mølgaard Plasc

Problem



Når jeg programmerede en slags backup applikation, gjorde jeg en evaluering af filkopiering ydeevne på Windows.


Jeg har flere spørgsmål, og jeg spekulerer på dine meninger.


Tak skal du have!


Lucas.


Spørgsmål:



  1. Hvorfor er ydeevnen så meget langsommere, når du kopierer 10 GiB-filen i forhold til 1 GiB-filen?

  2. Hvorfor er shutil.copyfile så langsom?

  3. Hvorfor er win32file.CopyFileEx så langsom?
    Kunne dette skyldes flag win32file.COPY\_FILE\_RESTARTABLE?
    Men det accepterer ikke int 1000 som flag (COPY\_FILE\_NO\_BUFFERING),
    som anbefales til store filer:
    http://msdn.microsoft.com/en-us/library/aa363852\%28VS.85\%29.aspx[7]

  4. Brug af en tom ProgressRoutine ser ud til at have nogen indflydelse over, at du ikke bruger nogen ProgressRoutine overhovedet.

  5. Er der en alternativ og bedre måde at kopiere filerne på, men også at få fremskridt opdateringer?



Resultater for en 1 GiB og en 10 GiB-fil:


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


Testmiljø:


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


Bemærkninger:


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


win32file.CopyFileEx A (bruger ikke 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 (ved hjælp af tom 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

Bedste reference


Spørgsmål 3:



Du misforstærker COPY\_FILE\_NO\_BUFFERING-flagget i Microsofts API.
Det er ikke int 1000, men hex 1000 (0x1000 => int værdi: 4096). Når du indstiller CopyFlags=4096, får du den (?) Hurtigste kopieringsrutine i et Windows-miljø.
Jeg bruger den samme rutine i min data backup kode, som er meget hurtig og overfører terabyte størrelse data dag til dag.


Spørgsmål 4:



Det betyder ikke noget som det er en tilbagekaldelse. Men generelt skal du ikke lægge for meget kode inde og holde det rent og glat.


Spørgsmål 5:



Efter min erfaring er det den hurtigst mulige kopieringsrutine i et standard Windows-miljø. Der kan være hurtigere brugerdefinerede kopieringsrutiner, men når der bruges almindeligt Windows API, kan der ikke findes noget bedre.

Andre referencer 1


Med al sandsynlighed, fordi du 'måler færdiggørelsestiden forskelligt.


Jeg gætter på, at en 1GB-fil passer komfortabelt til rammen. Derfor slipper operativsystemet sandsynligvis bare caching det og fortæller din applikation, det kopieres, når det meste af det (måske alle) stadig er ubremset i kernelbufferne.


Imidlertid passer 10G-filen ikke i ram, så den skal skrive (det meste af) den, før den siger, at den er færdig.


Hvis du vil have en meningsfuld måling,


a) Ryd filsystembuffer cachen før hvert løb - hvis dit operativsystem ikke er en bekvem måde at gøre dette på, skal du genstarte (NB: Windows giver ikke en bekvem metode, jeg tror, ​​at der findes et system internals værktøj, der gør dette dog) . I tilfælde af et netværk filsystem fjerner også cachen på serveren.


b) Synkroniser filen til disken, når du er færdig, inden du måler færdiggørelsestiden


Så jeg forventer, at du vil se mere konsekvente tider.

Andre referencer 2


At besvare dit spørgsmål 2 .:


shutil.copyfile () er så langsom, fordi det som standard bruger en 16Kbyte kopibuffer. Til sidst ender det i shutil.copyfileobj (), som ser sådan ud:


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)


I dit tilfælde er det ping-ponging mellem at læse 16K og skrive 16K. Hvis du skulle bruge copyfileobj () direkte på din GB-fil, men med en buffer på 128 MB for eksempel, ville du se drastisk forbedret ydeevne.