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