Как сделать поиск быстрее? С#
- Привет! Кто-нибудь может сказать, как сделать этот код быстрее?
Этот поиск занимает слишком много времени... 700 000 файлов за 15 минут...
Я буду благодарен за каждую микрооптимизацию!
Спасибо!
Search Class: using System; using System.Collections.Generic; using System.ComponentModel; using System.IO; using System.Runtime.InteropServices; public class FindFile { private static readonly char[] InvalidFilePathChars; private static readonly char[] InvalidFilePatternChars; static FindFile() { InvalidFilePathChars = Path.GetInvalidPathChars(); List<char> set = new List<char>(Path.GetInvalidFileNameChars()); set.Remove('*'); set.Remove('?'); InvalidFilePatternChars = set.ToArray(); } // Enumerates the files directly in the directory specified public static void FilesIn(string directory, Action<FileFoundEventArgs> e) { FindFile ff = new FindFile(directory, STAR, false, false, true); ff.FileFound = (o, a) => e(a); ff.Find(); } //Enumerates the folders directly in the directory specified public static void FoldersIn(string directory, Action<FileFoundEventArgs> e) { FindFile ff = new FindFile(directory, STAR, false, true, false); ff.FileFound = (o, a) => e(a); ff.Find(); } //Enumerates the files and folders directly in the directory specified public static void FilesAndFoldersIn(string directory, Action<FileFoundEventArgs> e) { FindFile ff = new FindFile(directory, STAR, false, true, true); ff.FileFound = (o, a) => e(a); ff.Find(); } //Enumerates the files anywhere under the directory specified public static void AllFilesIn(string directory, Action<FileFoundEventArgs> e) { FindFile ff = new FindFile(directory, STAR, true, false, true); ff.FileFound = (o, a) => e(a); ff.Find(); } //Enumerates the folders anywhere under the directory specified public static void AllFoldersIn(string directory, Action<FileFoundEventArgs> e) { FindFile ff = new FindFile(directory, STAR, true, true, false); ff.FileFound = (o, a) => e(a); ff.Find(); } //Enumerates the files and folders anywhere under the directory specified public static void AllFilesAndFoldersIn(string directory, Action<FileFoundEventArgs> e) { FindFile ff = new FindFile(directory, STAR, true, true, true); ff.FileFound = (o, a) => e(a); ff.Find(); } #region Kernel32 internal static class Kernel32 { internal const int MAX_PATH = 260; internal const int MAX_ALTERNATE = 14; internal const int ERROR_FILE_NOT_FOUND = 2; internal const int ERROR_PATH_NOT_FOUND = 3; internal const int ERROR_ACCESS_DENIED = 5; [StructLayout(LayoutKind.Sequential)] public struct FILETIME { public uint dwLowDateTime; public uint dwHighDateTime; public DateTime ToDateTimeUtc() { return DateTime.FromFileTimeUtc(dwLowDateTime | ((long)dwHighDateTime << 32)); } }; [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; //changed all to uint from int, otherwise you run into unexpected overflow public uint nFileSizeLow; private uint dwReserved0; private uint dwReserved1; [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_PATH)] public char[] cFileName; [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_ALTERNATE)] private char[] cAlternateFileName; public bool IgnoredByName { get { return (cFileName[0] == ZERO) || (cFileName[0] == '.' && cFileName[1] == ZERO) || (cFileName[0] == '.' && cFileName[1] == '.' && cFileName[2] == ZERO); } } } public enum FINDEX_INFO_LEVELS { FindExInfoStandard = 0, FindExInfoBasic = 1 } public enum FINDEX_SEARCH_OPS { FindExSearchNameMatch = 0, FindExSearchLimitToDirectories = 1, FindExSearchLimitToDevices = 2 } [Flags] public enum FINDEX_ADDITIONAL_FLAGS { FindFirstExCaseSensitive = 1, FindFirstExLargeFetch = 2, } [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] public static extern IntPtr FindFirstFileEx( IntPtr lpFileName, FINDEX_INFO_LEVELS fInfoLevelId, out WIN32_FIND_DATA lpFindFileData, FINDEX_SEARCH_OPS fSearchOp, IntPtr lpSearchFilter, FINDEX_ADDITIONAL_FLAGS dwAdditionalFlags); [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); } #endregion public struct Info { //Returns the parent folder's full path public string ParentPath { get { return Path.GetDirectoryName(FullPath); } } //Gets or sets the full path of the file or folder public string FullPath { get; set; } //Returns the file or folder name (with extension) public string Name { get { return Path.GetFileName(FullPath); } } //Returns the extenion or String.Empty public string Extension { get { return Path.GetExtension(FullPath); } } //Returns the UNC path to the parent folder public string ParentPathUnc { get { return (FullPath.StartsWith(@"\\")) ? ParentPath : (UncPrefix + ParentPath); } } //Returns the UNC path to the file or folder public string FullPathUnc { get { return (FullPath.StartsWith(@"\\")) ? FullPath : (UncPrefix + FullPath); } } //Gets or sets the length in bytes public long Length { get; set; } //Gets or sets the file or folder attributes public FileAttributes Attributes { get; set; } //Gets or sets the file or folder CreationTime in Utc public DateTime CreationTimeUtc { get; set; } //Gets or sets the file or folder LastAccessTime in Utc public DateTime LastAccessTimeUtc { get; set; } //Gets or sets the file or folder LastWriteTime in Utc public DateTime LastWriteTimeUtc { get; set; } } internal class Win32FindData { public char[] Buffer; public IntPtr BufferAddress; public Kernel32.WIN32_FIND_DATA Value; } // Provides access to the file or folder information durring enumeration, DO NOT keep a reference to this // class as it's meaning will change durring enumeration. public sealed class FileFoundEventArgs : EventArgs { private readonly Win32FindData _ff; private int _uncPrefixLength; private int _parentNameLength; private int _itemNameLength; private bool _cancelEnumeration; internal FileFoundEventArgs(Win32FindData ff) { _ff = ff; } internal void SetNameOffsets(int uncPrefixLength, int parentIx, int itemIx) { _parentNameLength = parentIx; _itemNameLength = itemIx; _uncPrefixLength = uncPrefixLength; } //Returns the parent folder's full path public string ParentPath { get { return new String(_ff.Buffer, _uncPrefixLength, _parentNameLength - _uncPrefixLength); } } //Returns the UNC path to the parent folder public string ParentPathUnc { get { return new String(_ff.Buffer, 0, _parentNameLength); } } //Gets the full path of the file or folder public string FullPath { get { return new String(_ff.Buffer, _uncPrefixLength, _itemNameLength - _uncPrefixLength); } } //Returns the UNC path to the file or folder public string FullPathUnc { get { return new String(_ff.Buffer, 0, _itemNameLength); } } //Returns the file or folder name (with extension) public string Name { get { return new String(_ff.Buffer, _parentNameLength, _itemNameLength - _parentNameLength); } } //Returns the extenion or String.Empty public string Extension { get { for (int ix = _itemNameLength; ix > _parentNameLength; --ix) if (_ff.Buffer[ix] == '.') return new String(_ff.Buffer, ix, _itemNameLength - ix); return string.Empty; } } //Gets the length in bytes public long Length { get { return _ff.Value.nFileSizeLow | ((long)_ff.Value.nFileSizeHigh << 32); } } //Gets the file or folder attributes public FileAttributes Attributes { get { return _ff.Value.dwFileAttributes; } } //Gets the file or folder CreationTime in Utc public DateTime CreationTimeUtc { get { return _ff.Value.ftCreationTime.ToDateTimeUtc(); } } //Gets the file or folder LastAccessTime in Utc public DateTime LastAccessTimeUtc { get { return _ff.Value.ftLastAccessTime.ToDateTimeUtc(); } } //Gets the file or folder LastWriteTime in Utc public DateTime LastWriteTimeUtc { get { return _ff.Value.ftLastWriteTime.ToDateTimeUtc(); } } //Returns true if the file or folder is ReadOnly public bool IsReadOnly { get { return (Attributes & FileAttributes.ReadOnly) != 0; } } //Returns true if the file or folder is Hidden public bool IsHidden { get { return (Attributes & FileAttributes.Hidden) != 0; } } //Returns true if the file or folder is System public bool IsSystem { get { return (Attributes & FileAttributes.System) != 0; } } //Returns true if the file or folder is Directory public bool IsDirectory { get { return (Attributes & FileAttributes.Directory) != 0; } } //Returns true if the file or folder is ReparsePoint public bool IsReparsePoint { get { return (Attributes & FileAttributes.ReparsePoint) != 0; } } //Returns true if the file or folder is Compressed public bool IsCompressed { get { return (Attributes & FileAttributes.Compressed) != 0; } } //Returns true if the file or folder is Offline public bool IsOffline { get { return (Attributes & FileAttributes.Offline) != 0; } } //Returns true if the file or folder is Encrypted public bool IsEncrypted { get { return (Attributes & FileAttributes.Encrypted) != 0; } } // Captures the current state as a <see cref="FindFile.Info"/> structure. public Info GetInfo() { return new Info { FullPath = FullPath, Length = Length, Attributes = Attributes, CreationTimeUtc = CreationTimeUtc, LastAccessTimeUtc = LastAccessTimeUtc, LastWriteTimeUtc = LastWriteTimeUtc, }; } //Gets or sets the Cancel flag to abort the current enumeration public bool CancelEnumeration { get { return _cancelEnumeration; } set { _cancelEnumeration = value; } } } private const string STAR = "*"; private const char SLASH = '\\'; private const char ZERO = '\0'; //Returns the Unc path prefix used public const string UncPrefix = @"\\?\"; private readonly Win32FindData _ff; private char[] _fpattern; private int _baseOffset; private bool _recursive; private bool _includeFolders; private bool _includeFiles; private bool _isUncPath; //Creates a FindFile instance. public FindFile() : this(UncPrefix, STAR, true, true, true) { } //Creates a FindFile instance. public FindFile(string rootDirectory) : this(rootDirectory, STAR, true, true, true) { } //Creates a FindFile instance. public FindFile(string rootDirectory, string filePattern) : this(rootDirectory, filePattern, true, true, true) { } //Creates a FindFile instance. public FindFile(string rootDirectory, string filePattern, bool recursive) : this(rootDirectory, filePattern, recursive, true, true) { } //Creates a FindFile instance. public FindFile(string rootDirectory, string filePattern, bool recursive, bool includeFolders) : this(rootDirectory, filePattern, recursive, includeFolders, true) { } //Creates a FindFile instance. public FindFile(string rootDirectory, string filePattern, bool recursive, bool includeFolders, bool includeFiles) { if (String.IsNullOrEmpty(rootDirectory) || String.IsNullOrEmpty(filePattern)) throw new ArgumentException(); _ff = new Win32FindData(); _ff.BufferAddress = IntPtr.Zero; _ff.Buffer = new char[0x1000]; _ff.Value = new Kernel32.WIN32_FIND_DATA(); _recursive = recursive; _includeFolders = includeFolders; _includeFiles = includeFiles; BaseDirectory = rootDirectory; FilePattern = filePattern; } // The event-handler to raise when a file or folder is found public event EventHandler<FileFoundEventArgs> FileFound; // Gets or sets the maximum number of allowed characters in a complete path, default = 4kb public int MaxPath { get { return _ff.Buffer.Length; } set { Array.Resize(ref _ff.Buffer, Check.InRange(value, Kernel32.MAX_PATH, 0x100000)); } } private int UncPrefixLength { get { return _isUncPath ? 4 : 0; } } //Gets or sets the base directory to search within public string BaseDirectory { get { return new String(_ff.Buffer, UncPrefixLength, _baseOffset - UncPrefixLength); } set { if (value.IndexOfAny(InvalidFilePathChars) > 0) throw new InvalidOperationException("Invalid characters in path."); if (!value.StartsWith(@"\\")) value = UncPrefix + value; if (!value.EndsWith(@"\")) value += @"\"; _isUncPath = value.StartsWith(UncPrefix); value.CopyTo(0, _ff.Buffer, 0, _baseOffset = value.Length); } } // Gets or sets the file pattern to match while enumerating files and folders. public string FilePattern { get { return new String(_fpattern); } set { if (value.IndexOfAny(InvalidFilePatternChars) >= 0) throw new InvalidOperationException("Invalid characters in pattern"); _fpattern = value.TrimStart(SLASH).ToCharArray(); } } //Gets or sets the Recursive flag public bool Recursive { get { return _recursive; } set { _recursive = value; } } //Gets or sets the IncludeFiles flag public bool IncludeFiles { get { return _includeFiles; } set { _includeFiles = value; } } //Gets or sets the IncludeFolders flag public bool IncludeFolders { get { return _includeFolders; } set { _includeFolders = value; } } //Gets or sets the RaiseOnAccessDenied flag, when set to true an 'Access Denied' can be raised public bool RaiseOnAccessDenied { get; set; } //Performs the search raising the FileFound event for each entry matching the request public void Find(string pattern) { FilePattern = pattern; Find(); } //Performs the search raising the FileFound event for each entry matching the request public void Find() { Check.NotNull(FileFound); GCHandle hdl = GCHandle.Alloc(_ff.Buffer, GCHandleType.Pinned); try { FileFoundEventArgs args = new FileFoundEventArgs(_ff); _ff.BufferAddress = hdl.AddrOfPinnedObject(); FindFileEx(args, _baseOffset); } finally { _ff.BufferAddress = IntPtr.Zero; hdl.Free(); } } private bool IsWild() { return (_fpattern.Length == 1 && _fpattern[0] == '*') || (_fpattern.Length == 3 && _fpattern[0] == '*' && _fpattern[1] == '.' && _fpattern[2] == '*'); } private void FindFileEx(FileFoundEventArgs args, int slength) { Kernel32.FINDEX_INFO_LEVELS findInfoLevel = Kernel32.FINDEX_INFO_LEVELS.FindExInfoStandard; Kernel32.FINDEX_ADDITIONAL_FLAGS additionalFlags = 0; if (Environment.OSVersion.Version.Major >= 6) { //Ignore short-names findInfoLevel = Kernel32.FINDEX_INFO_LEVELS.FindExInfoBasic; //Use large fetch table additionalFlags = Kernel32.FINDEX_ADDITIONAL_FLAGS.FindFirstExLargeFetch; } _fpattern.CopyTo(_ff.Buffer, slength); _ff.Buffer[slength + _fpattern.Length] = ZERO; IntPtr hFile = Kernel32.FindFirstFileEx( _ff.BufferAddress, findInfoLevel, out _ff.Value, Kernel32.FINDEX_SEARCH_OPS.FindExSearchNameMatch, IntPtr.Zero, additionalFlags); if ((IntPtr.Size == 4 && hFile.ToInt32() == -1) || (IntPtr.Size == 8 && hFile.ToInt64() == -1L)) { Win32Error(Marshal.GetLastWin32Error()); return; } bool traverseDirs = _recursive && IsWild(); try { do { int sposition = slength; for (int ix = 0; ix < Kernel32.MAX_PATH && sposition < _ff.Buffer.Length && _ff.Value.cFileName[ix] != 0; ++ix) _ff.Buffer[sposition++] = _ff.Value.cFileName[ix]; if (sposition == _ff.Buffer.Length) throw new PathTooLongException(); if (!_ff.Value.IgnoredByName) { bool isDirectory = (_ff.Value.dwFileAttributes & FileAttributes.Directory) == FileAttributes.Directory; if ((_includeFolders && isDirectory) || (_includeFiles && !isDirectory)) { args.SetNameOffsets(UncPrefixLength, slength, sposition); FileFound(this, args); } if (traverseDirs && isDirectory) { _ff.Buffer[sposition++] = SLASH; FindFileEx(args, sposition); } } } while (!args.CancelEnumeration && Kernel32.FindNextFile(hFile, out _ff.Value)); } finally { Kernel32.FindClose(hFile); } // Recursive search for patterns other than '*' and '*.*' requires we enum directories again if (_recursive && !traverseDirs) { _ff.Buffer[slength] = '*'; _ff.Buffer[slength + 1] = ZERO; hFile = Kernel32.FindFirstFileEx( _ff.BufferAddress, findInfoLevel, out _ff.Value, Kernel32.FINDEX_SEARCH_OPS.FindExSearchNameMatch, IntPtr.Zero, additionalFlags); if ((IntPtr.Size == 4 && hFile.ToInt32() == -1) || (IntPtr.Size == 8 && hFile.ToInt64() == -1L)) { Win32Error(Marshal.GetLastWin32Error()); return; } try { do { int sposition = slength; for (int ix = 0; ix < Kernel32.MAX_PATH && sposition < _ff.Buffer.Length && _ff.Value.cFileName[ix] != 0; ++ix) _ff.Buffer[sposition++] = _ff.Value.cFileName[ix]; if (sposition == _ff.Buffer.Length) throw new PathTooLongException(); if (!_ff.Value.IgnoredByName) { bool isDirectory = (_ff.Value.dwFileAttributes & FileAttributes.Directory) == FileAttributes.Directory; if (isDirectory) { _ff.Buffer[sposition++] = SLASH; FindFileEx(args, sposition); } } } while (!args.CancelEnumeration && Kernel32.FindNextFile(hFile, out _ff.Value)); } finally { Kernel32.FindClose(hFile); } } } private void Win32Error(int errorCode) { switch (errorCode) { case Kernel32.ERROR_FILE_NOT_FOUND: case Kernel32.ERROR_PATH_NOT_FOUND: return; case Kernel32.ERROR_ACCESS_DENIED: if (!RaiseOnAccessDenied) return; goto default; default: throw new Win32Exception(errorCode); } } } Check Class: using System; using System.Reflection; [System.Diagnostics.DebuggerNonUserCode] static partial class Check { public static void Assert<TException>(bool condition) where TException : Exception, new() { if (!condition) throw new TException(); } public static void Assert<TException>(bool condition, string message) where TException : Exception, new() { if (!condition) { ConstructorInfo ci = typeof(TException).GetConstructor(new Type[] { typeof(string) }); if (ci != null) { TException e = (TException)ci.Invoke(new object[] { message }); throw e; } throw new TException(); } } public delegate Exception ExceptionBuilder(); public static void Assert(bool condition, ExceptionBuilder fnExceptionBuilder) { if (!condition) throw fnExceptionBuilder(); } public static void Assert<TException>(bool condition, string message, Exception innerException) where TException : Exception, new() { if (!condition) { ConstructorInfo ci = typeof(TException).GetConstructor(new Type[] { typeof(string), typeof(Exception) }); if (ci != null) { TException e = (TException)ci.Invoke(new object[] { message, innerException }); throw e; } throw new TException(); } } public static T NotNull<T>(T value) { if (value == null) throw new ArgumentNullException(); return value; } public static string NotEmpty(string value) { if (value == null) throw new ArgumentNullException(); if (value.Length == 0) throw new ArgumentOutOfRangeException(); return value; } public static Guid NotEmpty(Guid value) { if (value == Guid.Empty) throw new ArgumentOutOfRangeException(); return value; } public static T NotEmpty<T>(T value) where T : System.Collections.IEnumerable { if (value == null) throw new ArgumentNullException(); if (!value.GetEnumerator().MoveNext()) throw new ArgumentOutOfRangeException(); return value; } public static void IsEqual<T>(T a, T b) where T : IEquatable<T> { if (false == a.Equals(b)) throw new ArgumentException(); } public static void NotEqual<T>(T a, T b) where T : IEquatable<T> { if (true == a.Equals(b)) throw new ArgumentException(); } public static T[] ArraySize<T>(T[] value, int min, int max) { if (value == null) throw new ArgumentNullException(); if (value.Length < min || value.Length > max) throw new ArgumentOutOfRangeException(); return value; } public static T InRange<T>(T value, T min, T max) where T : IComparable<T> { if (value == null) throw new ArgumentNullException(); if (value.CompareTo(min) < 0) throw new ArgumentOutOfRangeException(); if (value.CompareTo(max) > 0) throw new ArgumentOutOfRangeException(); return value; } public static T IsAssignable<T>(object value) { return (T)IsAssignable(typeof(T), value); } public static object IsAssignable(Type toType, object fromValue) { NotNull(toType); if (fromValue == null) { if (toType.IsValueType) throw new ArgumentException(string.Format("Can not set value of type {0} to null.", toType)); } else IsAssignable(toType, fromValue.GetType()); return fromValue; } public static void IsAssignable(Type toType, Type fromType) { if (!NotNull(toType).IsAssignableFrom(NotNull(fromType))) throw new ArgumentException(string.Format("Can not set value of type {0} to a value of type {1}", toType, fromType)); } }
Что я уже пробовал:
..........
..........
..........
Patrice T
Можете ли вы объяснить, что делает этот код ?
Philippe Mori
Слишком много кода... для вопроса на форуме.
Philippe Mori
Если у вас есть проблемы с производительностью, то вы используете профилировщик!