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