我不想用正确的创建日期创建足够多的文件来做一个像样的基准测试,所以我做了一个更通用的版本,它需要一个开始和结束时间,并给出匹配的文件名称。让它给出昨天创建的文件的特定子字符串自然会随之而来。
我想出的最快的单线程纯 .NET 答案是:
private static IEnumerable<string> FilesWithinDates(string directory, DateTime minCreated, DateTime maxCreated)
{
foreach(FileInfo fi in new DirectoryInfo(directory).GetFiles())
if(fi.CreationTime >= minCreated && fi.CreationTime <= maxCreated)
yield return fi.Name;
}
我本来预计EnumerateFiles()
会稍微快一点,但结果会稍微慢一些(如果你通过网络可能会更好,但我没有测试过)。
有一点收获:
private static ParallelQuery<string> FilesWithinDates(string directory, DateTime minCreated, DateTime maxCreated)
{
return new DirectoryInfo(directory).GetFiles().AsParallel()
.Where(fi => fi.CreationTime >= minCreated && fi.CreationTime <= maxCreated)
.Select(fi => fi.Name);
}
但不多,因为它无助于实际调用GetFiles()
. 如果您没有要使用的内核,或者没有足够大的结果,GetFiles()
那只会让事情变得更糟(开销AsParallel()
大于并行过滤的好处)。另一方面,如果您也可以并行执行后续处理步骤,那么整体应用程序速度可能会提高。
这样做似乎没有意义,EnumerateFiles()
因为它似乎不能很好地并行化,因为它基于我将要讨论的相同方法,而且这本质上是串行的 - 需要先前的结果来产生下一个结果。
我得到的最快的是:
public const int MAX_PATH = 260;
public const int MAX_ALTERNATE = 14;
[StructLayoutAttribute(LayoutKind.Sequential)]
public struct FILETIME
{
public uint dwLowDateTime;
public uint dwHighDateTime;
public static implicit operator long(FILETIME ft)
{
return (((long)ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
}
};
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
public struct WIN32_FIND_DATA
{
public FileAttributes dwFileAttributes;
public FILETIME ftCreationTime;
public FILETIME ftLastAccessTime;
public FILETIME ftLastWriteTime;
public uint nFileSizeHigh;
public uint nFileSizeLow;
public uint dwReserved0;
public uint dwReserved1;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=MAX_PATH)]
public string cFileName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=MAX_ALTERNATE)]
public string cAlternate;
}
[DllImport("kernel32", CharSet=CharSet.Unicode)]
public static extern IntPtr FindFirstFile(string lpFileName, out WIN32_FIND_DATA lpFindFileData);
[DllImport("kernel32", CharSet=CharSet.Unicode)]
public static extern bool FindNextFile(IntPtr hFindFile, out WIN32_FIND_DATA lpFindFileData);
[DllImport("kernel32.dll")]
public static extern bool FindClose(IntPtr hFindFile);
private static IEnumerable<string> FilesWithinDates(string directory, DateTime minCreated, DateTime maxCreated)
{
long startFrom = minCreated.ToFileTimeUtc();
long endAt = maxCreated.ToFileTimeUtc();
WIN32_FIND_DATA findData;
IntPtr findHandle = FindFirstFile(@"\\?\" + directory + @"\*", out findData);
if(findHandle != new IntPtr(-1))
{
do
{
if(
(findData.dwFileAttributes & FileAttributes.Directory) == 0
&&
findData.ftCreationTime >= startFrom
&&
findData.ftCreationTime <= endAt
)
{
yield return findData.cFileName;
}
}
while(FindNextFile(findHandle, out findData));
FindClose(findHandle);
}
}
没有aFindClose()
承诺的事情是很冒险的IDisposable
,并且手动实现IEnumerator<string>
不仅应该使这更容易做到(这样做的严重原因),而且还希望像 3 纳秒或其他东西一样减少(不是这样做的严重理由) ,但上面显示了基本思想。