Более быстрый способ получить несколько FileInfo? - PullRequest
13 голосов
/ 04 декабря 2010

Это длинный снимок, но есть ли более быстрый способ получить размер, время последнего обращения, время последнего создания и т. Д. Для нескольких файлов?

У меня длинный список путей к файлам (поэтому мне не нужно перечислять), и мне нужно как можно быстрее найти эту информацию. Параллельное создание FileInfo, вероятно, не сильно поможет, поскольку узким местом должен быть диск.

Журнал NTFS, к сожалению, хранит только имена файлов, в противном случае это будет здорово, я думаю, ОС не хранит эту метаинформацию где-нибудь?

Еще одна оптимизация, которая может быть выполнена, если есть статический вызов или вызов Win32 (хотя методы File позволяют мне получать только один фрагмент информации за раз), который извлекает информацию, а не создает группу объектов FileInfo

В любом случае, я рад, что кто-то знает что-то, что может помочь, к сожалению, мне приходится проводить здесь микрооптимизацию, и никакое «использование базы данных» не является жизнеспособным ответом;)

Ответы [ 5 ]

9 голосов
/ 04 декабря 2010

На System.IO.File есть статические методы, чтобы получить то, что вы хотите. Это микрооптимизация, но это может быть то, что вам нужно: GetLastAccessTime , GetCreationTime .

Редактировать

Я оставлю текст выше, потому что вы специально попросили статические методы. Тем не менее, я думаю, что вам лучше использовать FileInfo (вы должны измерить просто чтобы быть уверенным). И File, и FileInfo используют внутренний метод File, который называется FillAttributeInfo, чтобы получить данные, которые вы ищете. Для нужных вам свойств FileInfo нужно будет вызвать этот метод один раз. File будет вызывать его при каждом вызове, поскольку объект информации атрибута выбрасывается после завершения метода (поскольку он статический).

Так что я догадываюсь, что когда вам нужно несколько атрибутов, FileInfo для каждого файла будет быстрее. Но в ситуациях с производительностью вы всегда должны измерять! Столкнувшись с этой проблемой, я бы попробовал оба варианта управления, как описано выше, и сделал тест, как при последовательном, так и параллельном запуске. Затем решите, достаточно ли это быстро.

Если это не достаточно быстро, вам нужно обратиться к Win32 API напрямую. Было бы не слишком сложно взглянуть на File.FileAttributeInfo в справочных источниках и придумать что-то похожее.

2-е редактирование

Фактически, если вам это действительно нужно, это код, необходимый для непосредственного вызова Win32 API, используя тот же подход, что и внутренний код для File, но используя один вызов ОС для получения всех атрибутов. Я думаю, что вы должны использовать, только если это действительно необходимо. Вам придется анализировать FILETIME на пригодную для использования дату и т. Д., Поэтому вам нужно будет выполнить дополнительную работу вручную.

static class FastFile
{
    private const int MAX_PATH = 260;
    private const int MAX_ALTERNATE = 14;

    public static WIN32_FIND_DATA GetFileData(string fileName)
    {
        WIN32_FIND_DATA data;
        IntPtr handle = FindFirstFile(fileName, out data);
        if (handle == IntPtr.Zero)
            throw new IOException("FindFirstFile failed");
        FindClose(handle);
        return data;
    }

    [DllImport("kernel32")]
    private static extern IntPtr FindFirstFile(string fileName, out WIN32_FIND_DATA data);

    [DllImport("kernel32")]
    private static extern bool FindClose(IntPtr hFindFile);


    [StructLayout(LayoutKind.Sequential)]
    public struct FILETIME
    {
        public uint dwLowDateTime;
        public uint dwHighDateTime;
    }
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    public struct WIN32_FIND_DATA
    {
        public FileAttributes dwFileAttributes;
        public FILETIME ftCreationTime;
        public FILETIME ftLastAccessTime;
        public FILETIME ftLastWriteTime;
        public int nFileSizeHigh;
        public int nFileSizeLow;
        public int dwReserved0;
        public int dwReserved1;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_PATH)]
        public string cFileName;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_ALTERNATE)]
        public string cAlternate;
    }
}
4 голосов
/ 04 декабря 2010

Классы DirectoryInfo и FileInfo в .NET работают невероятно медленно, особенно при использовании с общими сетевыми папками.

Если многие файлы для «сканирования» находятся в одном каталоге, вы получите многоболее быстрые результаты (в зависимости от ситуации: по измерениям быстрее) с использованием функций FindFirstFile, FindNextFile и FindClose API Win32.Это даже верно, если вам нужно запросить дополнительную информацию, которая вам действительно необходима (например, если вы запрашиваете все файлы «.log» в каталоге, где вам нужно только 75% из них).

На самом деле,Информационные классы .NET также используют эти функции Win32 API для внутреннего использования.Но они только «запоминают» имена файлов.При запросе дополнительной информации о группе файлов (например, LastModified) для каждого файла делается отдельный (сетевой) запрос, который требует времени.

2 голосов
/ 04 декабря 2010

Можно ли использовать класс DirectoryInfo?

 DirectoryInfo d = new DirectoryInfo(@"c:\\Temp");
 FileInfo[] f= d.GetFiles()
0 голосов
/ 05 июня 2019

Если файловая система удаленная, то может помочь параллелизм, поскольку узким местом может быть сеть.

Этот тестовый пример показал улучшение ~ 5х (52 с => 11 с) для файлов размером 50 КБ с использованием 8 потоков.Также было важно избежать блокировки (), так как вызов 50k имеет большое значение.Времена были сделаны без запуска отладчика.

Это также показывает, что работа по определению длины файла не выполняется до тех пор, пока не будет осуществлен доступ к FileInfo.Length.Доступ к длине снова после параллельной секции является мгновенным.Это может быть слишком сильно зависит от реализации.

// ~4s
//
List<string> files = Directory.EnumerateFileSystemEntries(directory, "*", SearchOption.AllDirectories)
    .ToList();

// ~0s
// 
Dictionary<string, FileInfo> fileMap = files.Select(file => new
{
    file,
    info = new FileInfo(file)
})
.ToDictionary(f => f.file, f => f.info);

// ~10s
//
Int64 totalSize = fileMap.Where(kv => kv.Value != null)
    .AsParallel() // ~50s w/o this 
    .Select(kv =>
    {
        try
        {
            return kv.Value.Length;
        }
        catch (FileNotFoundException)  // a transient file or directory
        {
        }
        catch (UnauthorizedAccessException)
        {
        }
        return 0;
    })
    .Sum();
0 голосов
/ 04 декабря 2010

Я думаю, вы ищете функцию GetFileAttributesEx ( pinvoke.net ссылка ) Однако класс FileInfo (точнее, его базовый класс) все равно использует это внутренне, поэтому я сомневаюсь, что вы увидите какое-либо улучшение производительности.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...