У меня есть следующая программа в 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 db.
Все работает отлично, за исключением очень большого использования памяти (около 800 м).
Прокомментировав код, я определил проблему как шаг геокодирования.
Я отправляю 100 адресов одновременно MapQuest api (https://developer.mapquest.com/documentation/geocoding-api/batch/get/).
Ответ на 100 адресов довольно массивный, поэтому он может быть одним из виновников, но 800M? Я чувствую, что он держится за все результаты до конца, которые так сильно влияют на использование памяти.
После комментирования часть геокодирования для использования памяти программы составляет около 30 М, что прекрасно.
Вы можете получить полную версию, которая воспроизводит этот вопрос здесь: https://github.com/Leonti/haskell-memory-so
Я совсем новичок в Haskell, так что не знаю, как я могу оптимизировать его.
Любые идеи?
Cheers!
Я подозреваю, что GC просто не влезает, потому что у вас достаточно доступной ОЗУ и не работает GC быстрее, чем бегать без необходимости. Это довольно распространенный образец на языках GC-ed. Попробуйте ограничить доступность кучи и посмотрите, подходит ли она. – 9000
@ 9000 Это вряд ли поможет. Вероятно, ПП справедлив в том, что промежуточные результаты проводятся слишком долго. «MapM» в 'propertiesWithGeocoding' является вероятным виновником (и, если это так, ответы здесь скорее всего будут включать потоковые библиотеки, такие как * pipe * и * conduit *, которые обычно используются для предоставления альтернатив' mapM' при работе с большими объемами данных). – duplode
Хотелось бы, чтобы у меня был бегущий фрагмент. Решение может частично состоять в использовании потоковой библиотеки, такой как «потоковая передача» или «канал» или «труба». Но есть и другие особенности. Имейте в виду, прежде всего, что аэзон имеет тенденцию накапливать весь ввод в памяти для синтаксического анализа. (Это единственный способ сделать это для всех json). Существует библиотека 'json-stream', которая может обойти это в некоторых случаях в зависимости от того, что вы ищете в json. – Michael