Comments 34
С другой стороны — ресурсы же всё-равно будут использованы, т.е. будут загружаться. Тогда почему бы не загрузить их с диска сразу, а уже по надобности брать их из массива с tar-данными.Вы один раз считываете сжатый файл, затем разжатый файл скармливаете своей функции, затем выхлоп фунции скармливаете тому, что должно обрабатывать конечный ресурс. Итого, если у вас задействован каждый файл, и каждый файл после загрузки находится в памяти, пиковое потребление оперативки превышает размер архива чуть больше, чем в два раза. Если бы индексироваться по самому файлу, не считывая его в память полностью, затраты памяти были бы меньше, т.к. хранилось бы только непосредственно прочитанное.
Я один раз читаю cжатый файл. Затем распаковываю архив. Это и есть пик, когда загружены и архив и его распакованная версия. Далее сжатый файл удаляется из памяти, во время работы приложения занято памяти ровно на распакованные tar-данные. Пример: data.tar 1000kb, data.tar.gz 250kb. Итого на диске хранится 250kb, пиковое потребление памяти 1250kb, потребление памяти в рабочем режиме — 1000kb. Другое дело, что если tar-файл будет большим, то грузить его весь в оперативку не рационально. Для таких случаев лучше присмотреть другое решение, а не dxTarRead.
0MiB — запустились
+40 MiB — прочитали сжатый файл (итого 40 MiB)
+1000 MiB — распаковали сжатый файл в память (итого 1040 MiB)
-40 MiB — выгрузили сжатый файл (итого 1000 MiB)
+200 MiB — загрузили текстуру 1/5 из файла (итого 1200 MiB)
+200 MiB — загрузили текстуру 2/5 из файла (итого 1400 MiB)
+200 MiB — загрузили текстуру 3/5 из файла (итого 1600 MiB)
+200 MiB — загрузили текстуру 4/5 из файла (итого 1800 MiB)
+200 MiB — загрузили текстуру 5/5 из файла (итого 2000 MiB)
-1000 MiB — выгрузили распакованный архив (итого 1000 MiB)
Пиковая загрузка, выходит, 2 GiB, рабочий режим — 1 GiB. Если бы библиотека позволяла итерироваться по файлу, то загрузка была бы следующей:
0MiB — запустились
+240 MiB — распаковали одну текстуру и скопировали в память (итого 240 MiB)
+200 MiB — загрузили текстуру 1/5 из памяти (итого 440 MiB)
-240 MiB — освободили память из-под текстуры и контекста распаковки (итого 200 MiB)
+240 MiB — распаковали одну текстуру и скопировали в память (итого 440 MiB)
+200 MiB — загрузили текстуру 2/5 из памяти (итого 640 MiB)
-240 MiB — освободили память из-под текстуры и контекста распаковки (итого 400 MiB)
+240 MiB — распаковали одну текстуру и скопировали в память (итого 640 MiB)
+200 MiB — загрузили текстуру 3/5 из памяти (итого 840 MiB)
-240 MiB — освободили память из-под текстуры и контекста распаковки (итого 600 MiB)
+240 MiB — распаковали одну текстуру и скопировали в память (итого 840 MiB)
+200 MiB — загрузили текстуру 4/5 из памяти (итого 1040 MiB)
-240 MiB — освободили память из-под текстуры и контекста распаковки (итого 800 MiB)
+240 MiB — распаковали одну текстуру и скопировали в память (итого 1040 MiB)
+200 MiB — загрузили текстуру 5/5 из памяти (итого 1240 MiB)
-240 MiB — освободили память из-под текстуры и контекста распаковки (итого 1000 MiB)
Итого в таком случае мы бы тратили память только на хранение контекста распаковки и сырых данных.
Да, здесь согласен. Получается, что у меня в оперативке хранятся лишние картинки в виде сырых данных. Так получилось, что JSON-строки сразу конвертирую в дерево объектов, а память после tar и gzip освобождаю. Но это только из-за того, что JSON нужен весь и сразу. Попробую в своём проекте переделать логику менеджера спрайтов, чтобы сразу при инициализации грузил все картинки в формат текстуры, тогда tar-архив можно будет сразу освободить.
Ну так не грузите файл, а используйте отображение в память. Там ОС сама разберётся, что и когда нужно.
Погодите-погодите, а зачем считывать архив в память целиком? Почему бы не использовать mmap? (это если не менять предложенный автором код, а вообще в данном случае лучше читать файл блоками фиксированного размера)
Если бы речь шла о .tar-файле на диске — никаких проблем, mmap туда просится естественным образом. Но как натравить mmap на результат распаковки какого-нибудь gzip?
Ну mmap тут, естественно, в пролете. Но ведь gzip это потоковый компрессор, можно разжимать файл по кусочкам и распаковывать tar. Тоже не нужно читать файл в память целиком, но да, это уже не 26 строчек.
Да, и ко всему вышеуказанному — реализация автора статьи однопроходная. Ты указываешь имя файла, которое нужно извлечь, а оно ищет его и извлекает. Это хороший подход, когда весь архив уже в памяти, но если его нужно для каждого файла переизвлекать из gzip, то лучше переписать эту часть логики на "встретил файл — спросил коллбэк, что с ним делать".
tar — не совсем архив в широко распространённом смысле этого слова. Данные в нём не сжимаются, а хранятся в открытом виде
Ну архив сам по себе не подразумевает обязательное сжатие — сжатием архива занимается компрессор. А задача архиватора — получить архив — то есть множество файлов упаковать в один
Именно! Это и пытался описать. Лично меня когда-то очень удивило, что архив — это не обязательно сжатие. Попытаюсь как-нибудь перефразировать, спасибо за замечание.
Например, как там убирается очень длинный путь к файлу?
ar -cq out.ar file1.txt file2.txt
tar оптимизирован для бэкапов блочных устройств на летны (tape). Отсюда блоки по 512 байт, что соответствует популярному размеру сектора дисков. Но если основной кейс это распаковка в память, то фича скорее не нужна.
Спасибо! Вроде бы формат не сильно отличается от tar. Позже попробую добавить и этот формат в свой "архиватор" на гитхаб.
Покрутил немножко. Я не вижу поддержки директорий, а без них совсем грустно.
И длина имени файла всего 16 символов.
P.S. Хаб "Я пиарюсь!" я видел. А где хаб "Я ищу работу"? :)
На верху страницы ссылка: Мой круг
Первая четверть XXI века близилась к завершению… С89 всё ещё подавался как преимущество.
А почему не преимущество? Да, в таком стандарте не очень комфортно писать. Но C89 почти гарантирует, что проект успешно скомпилируется в любом современном компиляторе, начиная от Clang и заканчивая восьмибитными микроконтроллерами. Писать свой проект можно на чём угодно, хоть на "Super-mega-boosted-python-java#". Но если есть желание использовать чужую библиотеку, и при этом она написана на C89, то есть приличная вероятность, что она без проблем подключится.
Кроме того, Си есть Си. Т.е. отличная производительность за счёт низкоуровневости языка.
Есть ведь новые стандарты языка.
Есть. Но какова вероятность, что компилятор для микроволновки будет поддерживать свежие стандарты? Или какие-нибудь KolibriOS/MenuetOS? В которых основным компилятором TCC, да ещё и лохматых годов выпуска, да ещё и сурово допиленный напильником. А С89 будет работать везде. Потому, что стандарт относительно простой, и при создании компиляторов Си в первую очередь стремятся к нему.
Получается, можно использовать tar как файловую систему для embedded-устройств!
Читаем tar за 26 строк ANSI C кода