Как эффективно написать большой файл в Haskell - PullRequest
1 голос
/ 12 марта 2019

Здравствуйте, я пытаюсь своевременно записать файл ~1GB. Есть ли какой-нибудь рекомендуемый метод. До сих пор процесс занимает где-то порядка десятков минут.Я ошибаюсь при использовании Text я должен использовать ByteString?(Я также использовал String)

    pt="d:\\data2.csv"
    cnt=400000000

    main::IO()
    main=do
        let payload=dat
        writeWithHandle pt dat


    dat::Text
    dat=Data.Text.pack "0744442339"


    writeWithHandle::FilePath->Text->IO()
    writeWithHandle path tx=do
        handle<-openFile path WriteMode
        writeTimes cnt handle dat

    writeTimes::Int->Handle->Text->IO()
    writeTimes cnt handle payload= forM_ ([0..cnt])  (\x->Data.Text.IO.hPutStrLn handle payload)

Я не понимаю, почему это занимает так много порядка десятков минут. Сначала я использовал writeFile, но я думал, что это будет означать постояннооткрывая и закрывая file для каждой строки, поэтому я использовал appendFile безрезультатно.

1 Ответ

2 голосов
/ 13 марта 2019

Я бы порекомендовал использовать для этого Builder, который является эффективным способом заполнения буферов и может быть записан непосредственно в дескриптор.

#!/usr/bin/env stack
-- stack --resolver ghc-8.6.4 script
{-# LANGUAGE OverloadedStrings #-}
import Data.ByteString.Builder (Builder, hPutBuilder)
import Data.Foldable (fold)
import System.IO (IOMode (WriteMode), withBinaryFile)

pt :: FilePath
pt = "data2.csv"

cnt :: Int
cnt = 400000000

main :: IO ()
main = writeWithHandle pt dat

dat :: Builder
dat = "0744442339"

writeWithHandle :: FilePath -> Builder -> IO ()
writeWithHandle path tx =
  withBinaryFile path WriteMode $ \h ->
  hPutBuilder h $ makeBuilder cnt tx

makeBuilder :: Int -> Builder -> Builder
makeBuilder cnt payload = fold $ replicate cnt $ payload <> "\n"

Вы можете оставить payload как Textвместо этого, если хотите, и конвертируйте в Builder, используя encodeUtf8Builder.

...