Haskell - чтение всей Lazy ByteString

Контекст: у меня есть функция, определенная в библиотеке toXlsx :: ByteString -> Xlsx (это ByteString из Data.ByteString.Lazy)

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

Прямо сейчас я читаю файл как bs <- Data.ByteString.Lazy.readfile file и в конце делаю Data.ByteString.Lazy.length bs 'seq' return value,

Есть ли способ использовать эту функцию и сохранить файл в памяти в целом для его повторного использования?

1 ответ

Решение

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

Например, если вы запустите следующую программу для большого файла:

import qualified Data.ByteString.Lazy as BL  
main = do
  bigFile <- BL.readFile "ubuntu-14.04-desktop-amd64.iso"
  print $ BL.length $ BL.filter (==0) bigFile     -- takes a while
  print $ BL.length $ BL.filter (==255) bigFile   -- runs fast

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

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

Если вас беспокоит то, что вы хотите, чтобы весь файл был прочитан в начале, даже если первой операции не нужно читать все это, чтобы не было никаких последующих задержек при чтении дополнительных частей файла, тогда ваш seq решение на основе, вероятно, хорошо. Кроме того, вы можете прочитать весь файл как строгую строку, а затем преобразовать его, используя fromStrict - эта операция является мгновенной и не копирует никаких данных. (В отличие от toStrict, который стоит дорого и копирует данные.) Так что это будет работать:

import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as BL

main = do
  -- read strict
  bigFile <- BS.readFile "whatever.mov"
  -- do strict and lazy operations
  print $ strictOp bigFile
  print $ lazyOp (BL.fromStrict bigFile)
Другие вопросы по тегам