Кэширование бинарного файла в C#

Можно ли кэшировать двоичный файл в .NET и выполнять обычные файловые операции с кэшированным файлом?


person Community    schedule 29.05.2009    source источник
comment
Хм? Что вы подразумеваете под 1) Кэш? 2) Двоичный файл (например, нетекстовый файл, исполняемый файл, изображение)? 3) Обычные файловые операции?   -  person Binary Worrier    schedule 29.05.2009
comment
Кроме того, почему вы хотите его кэшировать? Может быть, это ненужно?   -  person uriDium    schedule 29.05.2009
comment
дайте использовать вариант использования, пожалуйста.   -  person Preet Sangha    schedule 29.05.2009
comment
у меня есть двоичный файл, в котором я выполняю двоичный поиск по содержимому, и я довольно часто обращаюсь к нему на своей веб-странице. Моя идея заключалась в том, чтобы кэшировать его для поиска для быстрого доступа   -  person    schedule 29.05.2009
comment
Я искал это уже час. С какой стати никто не отвечает на вопрос как есть, и все предполагают, что есть умная ОС, и разработчик получает доступ к файлу локально? Я думаю. если вы не знаете, как эффективно кэшировать файл, независимо от того, какая ОС или размер файлов, нет необходимости предлагать потоки памяти. /rant жаль, что это были не только вы. Я видел несколько других вопросов/ответов с тем же материалом MemoryStream, который сводил меня с ума.   -  person Behrooz    schedule 08.02.2014


Ответы (5)


Способ сделать это состоит в том, чтобы прочитать все содержимое из FileStream в объект MemoryStream, а затем использовать этот объект для ввода-вывода позже. Оба типа наследуются от Stream, поэтому использование будет практически идентичным.

Вот пример:

private MemoryStream cachedStream;

public void CacheFile(string fileName)
{
    cachedStream = new MemoryStream(File.ReadAllBytes(fileName));
}

Поэтому просто вызовите метод CacheFile один раз, когда вы хотите кэшировать данный файл, а затем в любом другом месте кода используйте cachedStream для чтения. (Фактический файл будет закрыт, как только его содержимое будет кэшировано.) Единственное, что нужно помнить, это удалить cachedStream, когда вы закончите с ним.

person Noldorin    schedule 29.05.2009
comment
Вероятно, все будет хорошо - единственная проблема будет, если мы говорим о файле размером в ГБ или два. - person Daniel Earwicker; 29.05.2009
comment
Да, этот метод, конечно, перестает быть полезным, когда размер файла приближается к размеру оперативной памяти. Однако к этому моменту вы должны использовать сервер базы данных, поэтому я предполагаю, что здесь это не будет проблемой. - person Noldorin; 29.05.2009

Любая современная ОС имеет встроенную систему кэширования, так что фактически всякий раз, когда вы взаимодействуете с файлом, вы взаимодействуете с кешем файла в памяти.

Перед применением пользовательского кэширования необходимо задать важный вопрос: что происходит, когда базовый файл изменяется, и моя кэшированная копия становится недействительной?

Вы можете еще больше усложнить ситуацию, если разрешено изменять кэшированную копию, и эти изменения должны быть сохранены обратно в базовый файл.

Если файл небольшой, проще использовать MemoryStream, как это предлагается в другом ответе.

Если вам нужно сохранить изменения обратно в файл, вы можете написать класс-оболочку, который перенаправляет все в MemoryStream, но дополнительно имеет свойство IsDirty, которое устанавливается в значение true всякий раз, когда выполняется операция записи. Тогда у вас может быть некоторый управляющий код, который срабатывает всякий раз, когда вы выберете (в конце какой-то более крупной транзакции?), проверяет наличие (IsDirty == true) и сохраняет новую версию на диск. Это называется кэшированием «ленивой записи», поскольку изменения выполняются в памяти и фактически не сохраняются до определенного момента.

Если вы действительно хотите все усложнить или у вас очень большой файл, вы можете реализовать свою собственную разбивку по страницам, где вы выбираете размер буфера (может быть, 1 МБ?) и храните небольшое количество byte[] страниц этого фиксированного размера. На этот раз у вас будет грязный флаг для каждой страницы. Вы бы реализовали методы Stream, чтобы они скрывали детали от вызывающей стороны и извлекали (или отбрасывали) буферы страниц всякий раз, когда это необходимо.

Наконец, если вы хотите облегчить жизнь, попробуйте:

http://www.microsoft.com/Sqlserver/2005/en/us/compact.aspx

Он позволяет вам использовать тот же механизм SQL, что и SQL Server, но в файле, при этом все происходит внутри вашего процесса, а не через внешний сервер СУБД. Это, вероятно, даст вам гораздо более простой способ запроса и обновления вашего файла и избавит вас от необходимости написания большого количества кода сохраняемости от руки.

person Daniel Earwicker    schedule 29.05.2009
comment
Разве это не файл с отображением памяти (en.wikipedia.org/wiki/Memory-mapped_file) есть? Тем не менее, я думаю, что ОП хочет закрыть дескриптор файла как можно скорее. - person Noldorin; 29.05.2009
comment
Отображение памяти файла — это когда ОС использует файл (по вашему выбору) для предоставления резервного хранилища виртуальной памяти для области адресного пространства процесса. (Файл подкачки служит этой цели для нормального выделения памяти.) Я говорю о том, что в ОС есть дисковое кэширование, которое работает вне зависимости от того, как вы обращаетесь к файлу. Попробуйте использовать grep или что-то подобное для поиска в нескольких сотнях МБ текстовых файлов. Когда вы сделаете это во второй раз, это произойдет намного быстрее, и ваш жесткий диск не издаст ни звука, потому что все это находится в памяти. - person Daniel Earwicker; 29.05.2009
comment
@Earwicker: Да, я уверен, что ты прав. Тем не менее, копирование содержимого в MemoryStream кажется здесь лучшим решением, потому что а) оно не поддерживает блокировку файла; б) я подозреваю, что оно по-прежнему обеспечит прирост производительности. - person Noldorin; 29.05.2009

Ну, вы, конечно, можете прочитать файл в массив byte[] и начать с ним работать. И если вы хотите использовать поток, вы можете скопировать свой FileStream в MemoryStream и начать с ним работать, например:

public static void CopyStream( Stream input, Stream output )
{
        var buffer = new byte[32768];
        int readBytes;
        while( ( readBytes = input.Read( buffer, 0, buffer.Length ) ) > 0 )
        {
                output.Write( buffer, 0, readBytes );
        }
}

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

person tanascius    schedule 29.05.2009

Я не знаю, что именно вы делаете, но я предлагаю это предложение (которое может быть или не быть жизнеспособным в зависимости от того, что вы делаете):

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

person Giovanni Galbo    schedule 29.05.2009
comment
файл содержит много записей. на самом деле это двоичный файл базы данных страны maxmind - person ; 29.05.2009
comment
Исходя из этого, можем ли мы предположить, что реальная проблема заключается в том, что вы не получаете желаемой производительности от своих запросов? - person Sam Holder; 29.05.2009

В Lucene есть очень элегантная система кэширования, которая кэширует байты. с диска в память и интеллектуально обновляет хранилище и т. д. Возможно, вы захотите взглянуть на этот код, чтобы понять, как они это делают. Вы также можете прочитать об уровне хранения данных Microsoft SQL Server, так как команда MSSQL довольно откровенно рассказывает о некоторых наиболее важных деталях реализации.

person Jonathan C Dickinson    schedule 29.05.2009