gpt4 book ai didi

haskell - 减少 Haskell 程序的内存使用量

转载 作者:行者123 更新时间:2023-12-02 01:17:34 24 4
gpt4 key购买 nike

我在 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 db 中。
除了非常高的内存使用率(大约 800M)外,一切正常。
通过将代码注释掉,我已将问题定位为地理编码步骤。
我一次向 MapQuest api ( https://developer.mapquest.com/documentation/geocoding-api/batch/get/ ) 发送 100 个地址。
100 个地址的响应非常庞大,因此它可能是罪魁祸首之一,但是 800M?我觉得它会保留所有结果,直到最后导致内存使用率如此之高。

注释掉地理编码部分后,程序内存使用量约为 30M,这很好。

您可以在此处获得重现该问题的完整版本: https://github.com/Leonti/haskell-memory-so

enter image description here

我是 Haskell 的新手,所以不确定如何优化它。
有任何想法吗?

干杯!

最佳答案

值得记录的是,这竟然是一个 simple streaming problem因使用 mapM 而引起的和 sequence , 其中与 replicateMtraverse以及其他让你“从IO中提取列表”的事情总是引起积累的担忧。所以需要绕开流媒体库。所以在 repo 中只需要替换

processDate :: String -> IO ()
processDate date = do
allFiles <- listFiles date
allProperties <- mapM fileToProperties allFiles
let flattenedPropertiesWithPrice = filter hasPrice $ concat allProperties
geocodedProperties <- propertiesWithGeocoding flattenedPropertiesWithPrice
print geocodedProperties

propertiesWithGeocoding :: [ParsedProperty] -> IO [(ParsedProperty, Maybe LatLng)]
propertiesWithGeocoding properties = do
let batchProperties = chunksOf 100 properties
batchGeocodedLocations <- mapM geocodeAddresses batchProperties
let geocodedLocations = fromJust $ concat <$> sequence batchGeocodedLocations
return geocodedLocations

像这样的东西
import Streaming
import qualified Streaming.Prelude as S

processDate :: String -> IO ()
processDate date = do
allFiles <- listFiles date -- we accept an unstreamed list
S.print $ propertiesWithGeocoding -- this was the main pain point see below
$ S.filter hasPrice
$ S.concat
$ S.mapM fileToProperties -- this mapM doesn't accumulate
$ S.each allFiles -- the list is converted to a stream

propertiesWithGeocoding
:: Stream (Of ParsedProperty) IO r
-> Stream (Of (ParsedProperty, Maybe LatLng)) IO r
propertiesWithGeocoding properties =
S.concat $ S.concat
$ S.mapM geocodeAddresses -- this mapM doesn't accumulate results from mapquest
$ S.mapped S.toList -- convert segments to haskell lists
$ chunksOf 100 properties -- this is the streaming `chunksOf`
-- concat here flattens a stream of lists of as into a stream of as
-- and a stream of maybe as into a stream of as

那么内存使用看起来是这样的,每个峰值对应一次 Mapquest 的行程,紧接着是一些处理和打印,于是 ghc忘记这一切并继续前进:



当然,这可以通过 pipes 来完成。或 conduit .但在这里我们只需要一点简单的 mapM/ sequence/ traverse/ replicateM避免和 streaming对于这种快速的本地重构来说,这可能是最简单的。请注意,这个列表很短,所以想法“但是短列表很酷 mapM”/ traverse/etc !"可能是非常错误的。为什么不干脆摆脱它们?每当您要编写列表 mapM f 时,最好考虑 S.mapM f . S.each(或等效的导管或管道)。您现在将拥有一个流,可以使用 S.toList 或等效项恢复列表,但很可能在这种情况下,您会发现您不需要具体化的累积列表,但可以例如使用一些流处理,如打印到文件或在制作任何需要操作的列表之后,标准输出或将内容写入数据库(这里我们使用例如流 filterconcat 来扁平化流列表,并作为一种 catMaybe )。

关于haskell - 减少 Haskell 程序的内存使用量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41905139/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com