【发布时间】:2015-05-06 13:38:13
【问题描述】:
我正在进行一个项目,该项目需要复制大量文件和目录,同时保留其原始时间戳。所以我需要多次调用目标的SetCreationTime()、SetLastWriteTime() 和SetLastAccessTime() 方法,以便将原始值从源复制到目标。如下图所示,这些简单的操作占用了总计算时间的 42%。
由于这极大地限制了我的整个应用程序的性能,我想加快速度。我假设,这些调用中的每一个都需要打开和关闭文件/目录的新流。如果是这个原因,我想让这个流保持打开状态,直到我完成所有属性的编写。我该如何做到这一点?我想这需要使用一些 P/Invoke。
更新:
我按照 Lukas 的建议 使用 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);
}
}
可以找到我使用的枚举here。这使我只需打开文件一次即可完成所有操作:创建文件、应用所有属性、设置时间戳并从原始文件复制实际内容。
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);
}
值得付出努力吗?是的。它将计算时间从 86 秒减少到 51 秒,减少了约 40%。
优化前的结果:
优化后的结果:
【问题讨论】:
-
参见referencesource.microsoft.com/#mscorlib/system/io/… 了解
File.SetCreationTimeUtc的实现。您可能想要编写一个 P/InvokesCreateFile后跟SetFileTime的函数。
标签: c# winapi io filesystems