회사 일(모바일 앱)로 쓰는 작은 도구가 있는데, 분석 export 파일인 세션 이벤트 CSV를 읽어서 평균 세션 길이를 출력해요. 약 600만 줄에 대략 300MB예요. 답은 제대로 나와요. 그런데 전체 파일로 돌리면 오래된 Intel MacBook의 팬이 세게 돌고, +RTS -s는 최대 상주 메모리(maximum residency)를 1GB 훌쩍 넘게 보고해요.
처음엔 foldl로 짰다가 foldl'로 바꿨는데 최대 메모리는 거의 그대로였어요. -O2는 이미 cabal 파일에 들어 있고, 그것도 별 차이가 없었어요.
meanMillis :: [Session] -> Double
meanMillis ss
| n == 0 = 0
| otherwise = fromIntegral total / fromIntegral n
where
(total, n) = foldl' step (0 :: Int, 0 :: Int) ss
step (t, c) s = (t + sessMillis s, c + 1)
그리고 main에서는 이렇게 해요:
contents <- readFile (optPath opts)
let sessions = mapMaybe parseLine (drop 1 (lines contents))
Session은 String 하나와 Int 하나를 가진 평범한 레코드고, 화면별 필터는 그냥 filter예요.
첫 번째로 의심하는 건 readFile과 lines라서 hGetLine 루프를 생각해 봤는데, 아직 시도는 안 했어요. 파일을 lazy하게 소비하고 각 줄을 파싱한 뒤 버린다면 strict fold로 충분해야 하는데 왜 아닌지 모르겠어요. 이 머신에서는 프로파일링 빌드가 너무 느려서, 거의 -s 숫자만 보고 짐작하고 있어요.
메모리가 실제로 어디로 가는 걸까요?
영어에서 번역·원문 보기I've got a small tool for work (mobile app) that reads an analytics export, a CSV of session events, about 6 million lines and roughly 300MB, and prints the mean session length. It gives the right answer. But on the full file the fans on my old Intel MacBook spin up, and +RTS -s reports maximum residency of well over a gigabyte.
I wrote it with foldl first, then swapped in foldl', and peak memory barely moved. -O2 is already in the cabal file and made no real difference either.
meanMillis :: [Session] -> Double
meanMillis ss
| n == 0 = 0
| otherwise = fromIntegral total / fromIntegral n
where
(total, n) = foldl' step (0 :: Int, 0 :: Int) ss
step (t, c) s = (t + sessMillis s, c + 1)
and in main:
contents <- readFile (optPath opts)
let sessions = mapMaybe parseLine (drop 1 (lines contents))
Session is a plain record with a String and an Int, and the filter by screen is just filter.
My first suspect is readFile and lines, so I thought about an hGetLine loop, but I haven't tried it. I don't see why the strict fold wouldn't be enough if the file is consumed lazily and each line is dropped after parsing. A profiling build is very slow on this machine, so I'm mostly guessing from the -s numbers.
Where is the memory actually going?