我在Haskell一個下面的程序:減少一個Haskell程序的內存佔用
processDate :: String -> IO()
processDate date = do
...
let newFlattenedPropertiesWithPrice = filter (notYetInserted date existingProperties) flattenedPropertiesWithPrice
geocodedProperties <- propertiesWithGeocoding newFlattenedPropertiesWithPrice
propertiesWithGeocoding :: [ParsedProperty] -> IO [(ParsedProperty, Maybe LatLng)]
propertiesWithGeocoding properties = do
let addresses = fmap location properties
let batchAddresses = chunksOf 100 addresses
batchGeocodedLocations <- mapM geocodeAddresses batchAddresses
let geocodedLocations = fromJust $ concat <$> sequence batchGeocodedLocations
return (zip properties geocodedLocations)
geocodeAddresses :: [String] -> IO (Maybe [Maybe LatLng])
geocodeAddresses addresses = do
mapQuestKey <- getEnv "MAP_QUEST_KEY"
geocodeResponse <- openURL $ mapQuestUrl mapQuestKey addresses
return $ geocodeResponseToResults geocodeResponse
geocodeResponseToResults :: String -> Maybe [Maybe LatLng]
geocodeResponseToResults inputResponse =
latLangs
where
decodedResponse :: Maybe GeocodingResponse
decodedResponse = decodeGeocodingResponse inputResponse
latLangs = fmap (fmap geocodingResultToLatLng . results) decodedResponse
decodeGeocodingResponse :: String -> Maybe GeocodingResponse
decodeGeocodingResponse inputResponse = Data.Aeson.decode (fromString inputResponse) :: Maybe GeocodingResponse
它讀取從HTML文件屬性(住宅和公寓)的列表,分析它們,地理編碼地址並保存結果到sqlite數據庫。
除了非常高的內存使用率(大約800M)之外,一切正常。
通過對代碼進行評論,我指出問題是地理編碼步驟。
我一次發送100個地址到MapQuest API(https://developer.mapquest.com/documentation/geocoding-api/batch/get/)。
100個地址的響應非常大,所以它可能是其中一個匪徒,但800M?我感覺所有的結果都持續到驅動內存使用這麼高的結尾。
在註釋掉程序的地理編碼部分之後,內存使用量大約在30M左右,這很好。
你可以得到完整的版本,這裏再現了問題:https://github.com/Leonti/haskell-memory-so
我在Haskell相當新手,所以不知道如何優化它。
任何想法?
乾杯!
我懷疑GC沒有啓動,因爲你有足夠的內存可用,而不運行GC要比不必要地運行更快。在GC-ed語言中這是一種非常常見的模式。嘗試限制堆可用,看看它是否仍然適合。 – 9000
@ 9000這不太可能有幫助。關於中間結果持續太久,OP可能是正確的。 'propertiesWithGeocoding'中的'mapM'是一個可能的罪魁禍首(如果確實如此,這裏的答案可能涉及到流式庫,例如* pipes *和* conduit *,它們通常用於提供'mapM'的替代方案當處理大量數據時)。 – duplode
我希望我有一個可運行的片段。解決方案可能部分是使用流媒體庫,如「streaming」或「conduit」或「pipes」。但還有其他的特點。首先記住,aeson傾向於積累內存中用於解析的所有輸入。 (這是唯一可以適用於所有json的方法)。有一個'json-stream'庫可以在某些情況下解決這個問題,這取決於你在json中尋找什麼。 – Michael