5

ある種のバックアップアプリケーションをプログラミングするときに、Windowsでのファイルコピーのパフォーマンスを評価しました。

いくつか質問がありますが、ご意見をお聞かせください。

ありがとうございました!

ルーカス。

質問:

  1. 1GiBファイルと比較して10GiBファイルをコピーする場合のパフォーマンスが非常に遅いのはなぜですか?

  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. ファイルをコピーするだけでなく、進行状況の更新を取得するための、よりパフォーマンスの高い代替方法はありますか?

1GiBおよび10GiBファイルの結果:

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

4 に答える 4

3

おそらく、完了時間を別の方法で測定しているためです。

1GbファイルはRAMに快適に収まると思います。したがって、OSはおそらくそれをキャッシュし、そのほとんど(おそらくすべて)がカーネルバッファでまだフラッシュされていないときにコピーされたことをアプリケーションに通知しています。

ただし、10GファイルはRAMに収まらないため、終了する前に(ほとんどの)ファイルを書き込む必要があります。

意味のある測定が必要な場合は、

a)各実行の前にファイルシステムバッファキャッシュをクリアします-OSがこれを行う便利な方法を提供しない場合は、再起動します(注:Windowsは便利な方法を提供していませんが、これを行うシステム内部ツールがあると思います) 。ネットワークファイルシステムの場合は、サーバーのキャッシュもクリアします。

b)完了後、完了時間を測定する前に、ファイルをディスクに同期します

そうすれば、より一貫した時間が表示されることを期待しています。

于 2010-11-25T17:00:58.850 に答える
3

質問3:

MicrosoftAPIのCOPY_FILE_NO_BUFFERINGフラグを誤って解釈しています。これはint1000ではなくhex1000(0x1000 => int値:4096)です。CopyFlags = 4096に設定すると、Windows環境で(?)最速のコピールーチンが作成されます。私はデータバックアップコードで同じルーチンを使用しています。これは非常に高速で、テラバイトサイズのデータ​​を毎日転送します。

質問4:

コールバックなので問題ありません。ただし、全体として、内部にコードを入れすぎないようにし、クリーンで滑らかな状態に保つ必要があります。

質問5:

私の経験では、これは標準のWindows環境で可能な限り最速のコピールーチンです。より高速なカスタムコピールーチンがあるかもしれませんが、プレーンなWindows APIを使用する場合、これ以上良いものは見つかりません。

于 2014-12-08T16:52:27.023 に答える
2

あなたの質問に答えるには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のバッファーを使用すると、パフォーマンスが大幅に向上します。

于 2013-04-05T11:34:50.857 に答える
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)
于 2018-10-31T02:33:35.367 に答える