29 Read-only ресурсы

      Комментарии к записи 29 Read-only ресурсы отключены

Помечено: ,

В этой теме 0 ответов, 1 участник, последнее обновление  Васильев Владимир Сергеевич 4 мес., 2 нед. назад.

  • Автор
    Сообщения
  • #3607

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

    Каждый день мы используем приложения, количество read-only файлов в которых измеряется тысячами или даже десятками тысяч. Например — VMware Workstation, Adobe Photoshop, Microsoft Flight Simulator или VALVe Half-Life 2 (последняя, например, состоит из около 40 000 файлов).

    Современные файловые системы — это уже давно не просто «таблицы размещения файлов». Самые продвинутые файловые системы по сложности своей организации сравнимы с реляционными базами данных (некоторые из них, собственно, и являются самыми настоящими реляционными базами данных). Они обладают такой функциональностью как транзакционность, журналирование, индексирование, разграничение прав доступа и аудит, компрессия и декомпрессия, шифрование, и многое другое. Иными словами — файловые системы обладают навороченной функциональностью.

    Операции открытия и закрытия файла, а также итерирование файловой системы — это достаточно тяжелые операции, которые ложатся тяжелым грузом на жесткий диск (или иной носитель), если приложение решило раскидать свои ресурсы в несколько десятков тысяч файлов по несколько килобайт. Действительно, «укладывать» 100 мегабайт read-only ресурсов в 10 000 файлов это также глупо и безответственно, как хранить растровое изображение в std::list<std::list<pixel>>, или заполнять зарплатную смету завода, выдергивая каждую фамилию сотрудника отдельным SQL-запросом.

    Использование большого количества файлов небольшого размера расточительно — операционная система и носитель информации зачастую тратят гораздо больше времени и сил на управление доступом к файлам, чем непосредственно на чтение их содержимого. Читатель легко может убедиться в этом самостоятельно — для этого достаточно найти папку, состоящую из нескольких десятков тысяч файлов и скопировать ее в nul (или в /dev/null), после чего проделать тоже самое с одним файлом аналогичного размера и сравнить затраченное время. Программист, далекий от разработки в Ring0, чаще всего даже не подозревает, чего стоит жесткому диску прочитать файл в несколько килобайт; сколько чтений, позиционирований, вычислений контрольных сумм и всяческих проверок стоит за этой, казалось бы, простой операцией.

    Half-Life 2 состоит из 40 000 файлов либо потому что отдел тестирования VALVe никогда не устанавливал игру на среднестатистический компьютер целевой аудитории, либо потому что в отделе тестирования VALVe работают одни идиоты, либо сама VALVe в гробу видела проблемы своих пользователей.

    Файловая файловая система

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

    Read-only виртуальная файловая система может быть предельно упрощена. Во-первых, подавляющее большинство механизмов реальной файловой системы существует только потому, что в реальной файловой системе помимо чтения есть еще и запись. В нашей read-only системе эти механизмы по понятным причинам просто не нужны. Во-вторых, в 95% случаев в пределах одного приложения из всех (мета-) данных файловой системы используются только имя файла, его размер и содержимое. Все остальные метаданные — дата создания или изменения, атрибуты, права доступа, пользователи, группы, и так далее — в среднестатистическом приложении из 10 000 файлов просто не используются.

    В итоге наша read-only файловая система превращается в простой индексированный массив структур вида «имя, размер, смещение данных — имя, размер, смещение данных — …». При этом этот массив можно держать целиком в оперативной памяти, в результате чего любая файловая операция с точки зрения системного API будет состоять из одного позиционирования или из одного чтения (или и того и другого), что приведет к многократному росту производительности доступа к ресурсам.

    Сравним

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

    • За счет того, что носителю информации не нужно постоянно бегать от служебной информации к данным и обратно, аппаратные кеши и очереди оптимизации позиционирования используются более эффективно.
    • Множество файлов гораздо активнее расходует рабочий ресурс носителя информации. Иными словами — винт подохнет гораздо раньше.
    • Один файл оптимально использует кластеры файловой системы. Множество файлов оставляет за собой десятки мегабайт кластерных «хвостиков».
    • Множество файлов во время доступа заставляют операционную систему и носитель информации постоянно отрабатывать функциональность, которая в действительности приложению не нужна.
    • Копирование приложения, состоящего из нескольких файлов — это сказка. Копирования приложения, состоящего из 20 000 файлов — это тоже сказка, только китайская, когда в конце всех убивают, а остальным японцы делают сеппуку.

    Представим… как бы это могло быть.

    namespace rofs
    {
    
    template <class elem>
    class basic_archive
    {
    public:
    
        basic_archive(std::basic_string<elem> const& filename);
    
        // ...
    };
    
    template <elem>
    class basic_istream : public std::istream
    {
    public:
    
        basic_istream(basic_archive<elem> const& arc, std::basic_string<elem> const& filename);
    
        // ...
    };
    
    typedef basic_istream<char> istream;
    
    } // namespace rofs
    
    int main()
    {
        rofs::archive arc("resources.rofs");
    
        rofs::istream stream(arc, "/textures/skybox.tga");
    
        // ...
    }

    Правило

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

Для ответа в этой теме необходимо авторизоваться.