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.

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


person Nicolas S.    schedule 27.02.2018    source источник


Ответы (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)
person K. A. Buhr    schedule 27.02.2018
comment
Спасибо, вы не могли бы быть более полезным! Это именно то, что мне было нужно - person Nicolas S.; 27.02.2018