windows文件拷贝比较

    技术2022-07-04  127

    shell方式

    代码如下:

    void ShellCopyFiles(TCHAR* Src, TCHAR* Tgt) {     SHFILEOPSTRUCT op;     op.hwnd=NULL;     op.wFunc=FO_COPY;     op.pFrom=Src;     op.pTo=Tgt;     op.fFlags=FOF_ALLOWUNDO;     op.lpszProgressTitle=_T("Copying file");     SHFileOperation(&op); }

     

    拷贝1.44g文件122秒,不过在win7下由于权限的原因,如果指定目标是c盘根目录,会拷贝到C:/Users/xxx/AppData/Local下,看来执行程序时需要提升权限

     

    第二种是调用API CopyFile,试了四次,竟然是两次140秒左右,两次160秒左右。

    另外试了一下,如果目标是C盘根目录,表现和上面一样,如果不是C盘根目录下一个新建目录,拷贝的目标确实是函数指定的

     

    第三种是用开源软件FastCopy,测试结果是:85秒,67秒,77秒,确实很快

     

    第四种是用ReadFile和WriteFile,测试结果杯具了,202秒,把buffer改到32mb也差不多

    void SingleCopy(TCHAR* Src, TCHAR* Tgt) {     HANDLE hSrcFile = CreateFile(Src, GENERIC_READ, 0, NULL, OPEN_ALWAYS, 0, NULL);     HANDLE hDstFile = CreateFile(Tgt, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);     if( hSrcFile == INVALID_HANDLE_VALUE || hDstFile == INVALID_HANDLE_VALUE){         return;     }     DWORD dwRemainSize = GetFileSize(hSrcFile, NULL);     char buffer[1024];     while(dwRemainSize > 0){         DWORD dwActualRead = 0;         ReadFile(hSrcFile, buffer, 1024, &dwActualRead, NULL);         dwRemainSize -= dwActualRead;         DWORD dwActualWrote = 0;         while(dwActualWrote < dwActualRead){             DWORD dwOnceWrote = 0;             WriteFile(hDstFile, buffer + dwActualWrote, dwActualRead - dwActualWrote, &dwOnceWrote, NULL);             dwActualWrote += dwOnceWrote;         }             }         CloseHandle(hSrcFile);     CloseHandle(hDstFile); }

     

    第五种是用FileMapping ,如果每次映射大小为65536,结果在102秒左右,最长的是128秒,如果每次映射在32mb,则在160秒左右,看来这里每次映射大小对性能影响很大。要注意到是不能把整个文件映射进去,因为映射文件要求连续地址空间,即使空闲内存很多,也常常难以满足。性能可能也不一定好。如果要严格,要用VirtualQuery先查询。

    代码:

    void FileMappingCopy(TCHAR* Src, TCHAR* Tgt){     HANDLE hSrcFile = CreateFile(Src, GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, 0, NULL);     HANDLE hDstFile = CreateFile(Tgt, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);     if( hSrcFile == INVALID_HANDLE_VALUE || hDstFile == INVALID_HANDLE_VALUE){         return;     }     DWORD dwRemainSize = GetFileSize(hSrcFile, NULL);     DWORD dwFileSize = dwRemainSize;     HANDLE hSrcFileMapping = CreateFileMapping(hSrcFile, NULL, PAGE_READWRITE, 0, dwRemainSize, NULL);     HANDLE hDstFileMapping = CreateFileMapping(hDstFile, NULL, PAGE_READWRITE, 0, dwRemainSize, NULL);     if( hSrcFileMapping == INVALID_HANDLE_VALUE || hDstFileMapping == INVALID_HANDLE_VALUE){         return;     }     const int BUFFERBLOCKSIZE = 65536;     while(dwRemainSize > 0){         DWORD dwBlock = min(dwRemainSize, BUFFERBLOCKSIZE);         LPVOID pSrc = MapViewOfFile(hSrcFileMapping, FILE_MAP_ALL_ACCESS, 0, dwFileSize - dwRemainSize, dwBlock);         LPVOID pDst = MapViewOfFile(hDstFileMapping, FILE_MAP_ALL_ACCESS, 0, dwFileSize - dwRemainSize, dwBlock);         if( pSrc == NULL || pDst == NULL)         {             printf("fail/n");             return;         }         memcpy(pDst, pSrc, dwBlock);         UnmapViewOfFile(pSrc);         UnmapViewOfFile(pDst);         dwRemainSize -= dwBlock;     }         CloseHandle(hSrcFileMapping);     CloseHandle(hDstFileMapping);     CloseHandle(hSrcFile);     CloseHandle(hDstFile); }

     

    第六种方法是用overlapped读写,并使用多个缓冲区,见windows system programming 3rd 程序14-1

    结果令人失望,当用4个缓冲区,每个大小0x8000字节的时候,耗时553秒,后面也不想试了。如果用1个缓冲区,大小为0x20000,结果为177秒,用2个缓冲区,每个大小0x10000字节,结果653秒。猜测是频繁前后移动文件指针的原因

    代码:

    void OverLappedCopy(TCHAR* Src, TCHAR* Tgt) {     //const DWORD MAX_OVRLP = 4; /* Number of overlapped I/O operations. */     //const DWORD REC_SIZE = 0x8000; /* 32K: Minimum size for good performance. */     const DWORD MAX_OVRLP = 2; /* Number of overlapped I/O operations. */     const DWORD REC_SIZE = 0x10000; /* 32K: Minimum size for good performance. */     HANDLE hInputFile, hOutputFile;    /* There is a copy of each of the following variables and */    /* structures for each outstanding overlapped I/O operation. */    DWORD nin [MAX_OVRLP], nout [MAX_OVRLP], ic;    OVERLAPPED OverLapIn [MAX_OVRLP], OverLapOut [MAX_OVRLP];    /* The first event index is 0 for read, 1 for write. */    /* WaitForMultipleObjects requires a contiguous array. */    HANDLE hEvents [2] [MAX_OVRLP];    /* The first index on these two buffers is the I/O operation. */    CHAR AsRec [MAX_OVRLP] [REC_SIZE];       LARGE_INTEGER CurPosIn, CurPosOut, FileSize;    LONGLONG nRecord, iWaits;    hInputFile = CreateFile (Src, GENERIC_READ,          0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);    hOutputFile = CreateFile (Tgt, GENERIC_WRITE,          0, NULL, CREATE_ALWAYS, FILE_FLAG_OVERLAPPED, NULL);    /* Total number of records to process based on input file size. */    /* There may be a partial record at the end. */    FileSize.LowPart = GetFileSize (hInputFile,(LPDWORD)&FileSize.HighPart);    nRecord = FileSize.QuadPart / REC_SIZE;    if ((FileSize.QuadPart % REC_SIZE) != 0) nRecord++;    CurPosIn.QuadPart = 0;    for (ic = 0; ic < MAX_OVRLP; ic++) {       /* Create read and write events for each overlapped struct. */       hEvents [0] [ic] = OverLapIn [ic].hEvent /* Read event/struct.*/             = CreateEvent (NULL, TRUE, FALSE, NULL);       hEvents [1] [ic] = OverLapOut [ic].hEvent /* Write. */             = CreateEvent (NULL, TRUE, FALSE, NULL);       /* Initial file positions for each overlapped structure. */       OverLapIn [ic].Offset = CurPosIn.LowPart;       OverLapIn [ic].OffsetHigh = CurPosIn.HighPart;       /* Initiate an overlapped read for this overlapped struct. */       if (CurPosIn.QuadPart < FileSize.QuadPart)          ReadFile (hInputFile, AsRec [ic], REC_SIZE,                &nin [ic], &OverLapIn [ic]);       CurPosIn.QuadPart += (LONGLONG) REC_SIZE;    }    /* All read operations are running. Wait for an event to complete         and reset it immediately. Read and write events are         stored contiguously in the event array. */    iWaits = 0; /* Number of I/O operations completed so far. */    while (iWaits < 2 * nRecord) {       ic = WaitForMultipleObjects (2 * MAX_OVRLP,             hEvents [0], FALSE, INFINITE) - WAIT_OBJECT_0;       iWaits++; /* Increment # of complete I/O operations. */       ResetEvent (hEvents [ic / MAX_OVRLP] [ic % MAX_OVRLP]);       if (ic < MAX_OVRLP) { /* A read completed. */          GetOverlappedResult (hInputFile,                &OverLapIn [ic], &nin [ic], FALSE);          /* Process the record and initiate the write. */          CurPosIn.LowPart = OverLapIn [ic].Offset;          CurPosIn.HighPart = OverLapIn [ic].OffsetHigh;          CurPosOut.QuadPart =                (CurPosIn.QuadPart / REC_SIZE) * REC_SIZE;          OverLapOut [ic].Offset = CurPosOut.LowPart;          OverLapOut [ic].OffsetHigh = CurPosOut.HighPart;                   WriteFile (hOutputFile, AsRec [ic], nin [ic],                &nout [ic], &OverLapOut [ic]);          /* Prepare for the next read, which will be initiated             after the write, issued above, completes. */          CurPosIn.QuadPart +=                REC_SIZE * (LONGLONG) (MAX_OVRLP);          OverLapIn [ic].Offset = CurPosIn.LowPart;          OverLapIn [ic].OffsetHigh = CurPosIn.HighPart;       } else if (ic < 2 * MAX_OVRLP) { /* A write completed. */          /* Start the read. */          ic -= MAX_OVRLP; /* Set the output buffer index. */          if (!GetOverlappedResult (hOutputFile,                &OverLapOut [ic], &nout [ic], FALSE))             printf ("Read failed./n");          CurPosIn.LowPart = OverLapIn [ic].Offset;          CurPosIn.HighPart = OverLapIn [ic].OffsetHigh;          if (CurPosIn.QuadPart < FileSize.QuadPart) {             /* Start a new read. */             ReadFile (hInputFile, AsRec [ic], REC_SIZE,                   &nin [ic], &OverLapIn [ic]);          }       }    }    /* Close all events. */    for (ic = 0; ic < MAX_OVRLP; ic++) {       CloseHandle (hEvents [0] [ic]);       CloseHandle (hEvents [1] [ic]);    }    CloseHandle (hInputFile);    CloseHandle (hOutputFile);    }

     

    第七种方法是调用CreateFile的时候用FILE_FLAG_NO_BUFFERING参数,然后调用ReadFile和WriteFile。我参考了FastCopy代码,也是用的这个方法,每次读写的buffer是8mb,这个大小怎么确定还没研究。这个函数的运行时间在75秒左右

    代码如下:

    void NoBufferCopy(TCHAR* Src, TCHAR* Tgt) {     HANDLE hReadFile = CreateFile(Src, GENERIC_READ,  0, NULL, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING, NULL);     LARGE_INTEGER liFileSizeSrc;     LARGE_INTEGER processedSize = {0};     GetFileSizeEx(hReadFile, &liFileSizeSrc);     MemManager::GetInstance()->Init(512);        DWORD dwBlockSize = MemManager::GetInstance()->GetBlockSize();     HANDLE hWriteFile = CreateFile(Tgt, GENERIC_WRITE,  0, NULL, CREATE_ALWAYS, FILE_FLAG_NO_BUFFERING, NULL);     _int64    alloc_size = ALIGN_SIZE(liFileSizeSrc.QuadPart, dwBlockSize);     LONG    high_size = (LONG)(alloc_size >> 32);     ::SetFilePointer(hWriteFile, (LONG)alloc_size, &high_size, FILE_BEGIN);     ::SetEndOfFile(hWriteFile);     ::SetFilePointer(hWriteFile, 0, NULL, FILE_BEGIN);         while(processedSize.QuadPart < liFileSizeSrc.QuadPart ){         void* pMem = MemManager::GetInstance()->GetMemBlock();            DWORD dwReadSize = 0;         ReadFile(hReadFile, pMem, dwBlockSize, &dwReadSize, NULL);         DWORD dwWroteSize = 0;         WriteFile(hWriteFile, pMem, dwBlockSize, &dwWroteSize, NULL);             MemManager::GetInstance()->ReleaseMemBlock(pMem);             processedSize.QuadPart += dwReadSize;            }        ::SetFilePointer(hWriteFile, liFileSizeSrc.LowPart , &(liFileSizeSrc.HighPart), FILE_BEGIN);     ::SetEndOfFile(hWriteFile);     CloseHandle(hWriteFile);            MemManager::GetInstance()->DestroyInstance();     CloseHandle(hReadFile); }

     

    MemManager的类用于内存管理,分配内存采用的是VirtualAlloc方法

     

    全部代码在

    http://download.csdn.net/source/3006477

     


    最新回复(0)