wod*_*dzu 3 c# filesystems io winapi
我正在开发一个项目,它需要复制大量文件和目录,同时保留其原始时间戳.所以,我需要多次调用目标的SetCreationTime(),SetLastWriteTime()和SetLastAccessTime()方法,以原始值从源到目标的复制.如下面的屏幕截图所示,这些简单的操作占用了总计算时间的42%.

由于这极大地限制了我的整个应用程序的性能,我想加快速度.我假设,每个调用都需要打开和关闭文件/目录的新流.如果这就是原因,我想打开这个流,直到我写完所有属性.我该如何做到这一点?我想这需要使用一些P/Invoke.
更新:
我跟着卢卡斯建议使用WinAPI的方法CreateFile(..)用FILE_WRITE_ATTRIBUTES.为了P/Invoke我提到的以下包装方法:
public class Win32ApiWrapper
{
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern SafeFileHandle CreateFile(string lpFileName,
[MarshalAs(UnmanagedType.U4)] FileAccess dwDesiredAccess,
[MarshalAs(UnmanagedType.U4)] FileShare dwShareMode,
IntPtr lpSecurityAttributes,
[MarshalAs(UnmanagedType.U4)] FileMode dwCreationDisposition,
[MarshalAs(UnmanagedType.U4)] FileAttributes dwFlagsAndAttributes,
IntPtr hTemplateFile);
public static SafeFileHandle CreateFileGetHandle(string path, int fileAttributes)
{
return CreateFile(path,
(FileAccess)(EFileAccess.FILE_WRITE_ATTRIBUTES | EFileAccess.FILE_WRITE_DATA),
0,
IntPtr.Zero,
FileMode.Create,
(FileAttributes)fileAttributes,
IntPtr.Zero);
}
}
Run Code Online (Sandbox Code Playgroud)
我可以在这里找到我使用的枚举.这使我能够只打开文件一次完成所有事情:创建文件,应用所有属性,设置时间戳并从原始文件中复制实际内容.
FileInfo targetFile;
int fileAttributes;
IDictionary<string, long> timeStamps;
using (var hFile = Win32ApiWrapper.CreateFileGetHandle(targetFile.FullName, attributeFlags))
using (var targetStream = new FileStream(hFile, FileAccess.Write))
{
// copy file
Win32ApiWrapper.SetFileTime(hFile, timeStamps);
}
Run Code Online (Sandbox Code Playgroud)
值得努力吗?是.它将计算时间从86秒减少到51秒,减少了约40%.
优化前的结果:

优化后的结果:

我不是C#程序员,我不知道如何实现那些System.IO.FileSystemInfo方法.但我已经用WIN32 API函数SetFileTime(..)做了一些测试,它将在某些时候被C#调用.
这是我的基准测试循环的代码片段:
#define NO_OF_ITERATIONS 100000
int iteration;
DWORD tStart;
SYSTEMTIME tSys;
FILETIME tFile;
HANDLE hFile;
DWORD tEllapsed;
iteration = NO_OF_ITERATIONS;
GetLocalTime(&tSys);
tStart = GetTickCount();
while (iteration)
{
tSys.wYear++;
if (tSys.wYear > 2020)
{
tSys.wYear = 2000;
}
SystemTimeToFileTime(&tSys, &tFile);
hFile = CreateFile("test.dat",
GENERIC_WRITE, // FILE_WRITE_ATTRIBUTES
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
printf("CreateFile(..) failed (error: %d)\n", GetLastError());
break;
}
SetFileTime(hFile, &tFile, &tFile, &tFile);
CloseHandle(hFile);
iteration--;
}
tEllapsed = GetTickCount() - tStart;
Run Code Online (Sandbox Code Playgroud)
我已经看到设置文件时间的昂贵部分是文件的打开/关闭.大约60%的时间用于打开文件,大约40%用于关闭文件(需要将修改刷新到光盘).上述循环花费了大约9秒进行10000次迭代.
一个小小的研究表明,CreateFile(..)用FILE_WRITE_ATTRIBUTES(代替GENERIC_WRITE)调用足以改变文件的时间属性.
这种修改速度显着提升!现在,相同的循环在2秒内完成10000次迭代.由于迭代次数非常少,我已经进行了第二次100000次迭代运行,以获得更可靠的时间测量:
基于以上数字,我猜测C#方法在打开文件时使用了错误的访问模式以更改为文件时间.或者其他一些C#行为会减慢速度......
所以也许你的速度问题的解决方案是实现一个DLL导出一个C函数,它使用SetFileTime(..)?改变文件时间?或者你甚至可以导入函数CreateFile(..),SetFileTime(..)并CloseHandle(..)直接避免调用C#方法?
祝好运!
| 归档时间: |
|
| 查看次数: |
215 次 |
| 最近记录: |