Подтвердить что ты не робот

Более быстрый способ получить несколько файлов FileInfo?

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

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

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

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

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

4b9b3361

Ответ 1

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

Изменить

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

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

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

2nd Edit

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

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;
    }
}

Ответ 2

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

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

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

Ответ 3

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

 DirectoryInfo d = new DirectoryInfo(@"c:\\Temp");
 FileInfo[] f= d.GetFiles()

Ответ 4

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

Ответ 5

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

Этот тестовый пример показал улучшение ~ 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();