windows文件拷贝比较

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

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值