ATTOPARSEC выделяет тонну памяти на больших «взять»

StackOverflow https://stackoverflow.com/questions/4151265

  •  08-10-2019
  •  | 
  •  

Вопрос

Поэтому я пишу приложение нюхая пакета. По сути, я хотел, чтобы его понюхать за сессиями TCP, а затем разбирать их, чтобы увидеть, есть ли они http, и если они есть, и если у них есть правильный тип контента и т. Д., Сохранить их в виде файла на моем жестком диске.

Итак, в этот конец я хотел, чтобы это было эффективным. Поскольку текущая библиотека HTTP на основе строки, и я буду иметь дело с большими файлами, и мне действительно нужно только для разбора ответов HTTP, я решил катиться в ATTOPARSEC.

Когда я закончил свою программу, я обнаружил, что когда я разбираюсь на 9-мегонте HTTP-ответ с помощью файла WAV, когда я профилировал его, он выделял концерт памяти, когда он пытался выбрать тело отклика HTTP Отказ Когда я смотрю на http.prof, я вижу некоторые строки:

httpbody main 362 1 0.0 0.0 93,8 99.3 принимают данные. TATTOPARSEC.Internal 366 1201 0,0 0,0 93,8 99.3 Takewith Data.AttoParsec.internal 367 3603 0,0 0,0 93,8 99,3 3603 0,0 0,0 93,8 99,3 СКОРОЙШЕНИЕ DINDING DATA.ATTOPARSEC.INGERNAL 375 293 0.0 0.0 93.8 99.2 Подсказка Data.Attoparsec.internal 378 293 0,0 0,0 93,8 99,2 +++ data.attoparsec.internal 380 586 93.8 99.2 93,8 99.2

Так как вы можете видеть, где-то в Httpbody предприметю называется 1201 раз, вызывая 500+ (+++) составляющих бассейнов, что вызывает абсурдное количество распределения памяти.

Вот код. N - это просто длина содержимого HTTP-ответа, если есть один. Если нет ни одного, просто пытается взять все.

Я хотел, чтобы это вернуло ленивое погибшееся на 1000 человек или около того, но даже если я изменим его, чтобы просто взять N и вернуть строгий ByteString, у него все еще есть эти распределения в нем (и использует 14 гиг памяти).


httpBody n = do
  x <- if n > 0
    then AC.take n
    else AC.takeWhile (\_ -> True)
  if B.length x == 0
    then return Nothing
    else return (Just x)

Я читал блог парень, который делал комбинаторрент, и у него был такой же проблема, но я никогда не слышал о разрешении. Кто-нибудь когда-либо пробежал эту проблему до или нашел решение?

Редактировать: Хорошо, хорошо, я оставил это весь день и ничего не получил. После исследования проблемы, я не думаю, что есть способ сделать это, не добавляя ленивый BeetString Accessor в ATTOPARSEC. Я также смотрел на все остальные библиотеки, и им либо не хватало выступов или других вещей.

Поэтому я нашел обходной путь. Если вы думаете о HTTP-запросе, он идет заголовки, новая линия, новая линия, тело. Поскольку тело в последний раз, и разборка возвращает кортеж с тобой как то, что вы проанализировали, и что осталось от BYTESTRING, я могу пропустить анализ тела внутри ATTOPARSEC и вместо этого сбил корпус с постели.


parseHTTPs bs = if P.length results == 0
  then Nothing
  else Just results
  where results = foldParse(bs, [])

foldParse (bs,rs) = case ACL.parse httpResponse bs of
  ACL.Done rest r -> addBody (rest,rs) r
  otherwise ->  rs

addBody (rest,rs) http = foldParse (rest', rs')
  where
    contentlength = ((read . BU.toString) (maybe "0" id (hdrContentLength (rspHeaders http))))
    rest' = BL.drop contentlength rest
    rs' = rs ++ [http { rspBody = body' }]
    body'
      | contentlength == 0  = Just rest
      | BL.length rest == 0 = Nothing
      | otherwise           = Just (BL.take contentlength rest)
httpResponse = do
  (code, desc) <- statusLine
  hdrs <- many header
  endOfLine
--  body <- httpBody ((read . BU.toString) (maybe "0" id (hdrContentLength parsedHeaders)))

  return Response { rspCode = code, rspReason = desc, rspHeaders = parseHeaders hdrs,  rspBody = undefined }

Это немного грязно, но в конечном итоге он быстро работает и выделяет ничего больше, чем я хотел. Таким образом, в основном вы складываете по BYTESTRING, собирая структуры HTTP Data, затем между коллекциями, я проверяю длину содержимого структуры, которую я только что получил, вытяните соответствующую сумму из оставшегося BYTESTRING, а затем продолжайте влево, если влево.

Редактировать: Я на самом деле закончил этот проект. Работает как очарование. Я не вязализируюсь должным образом, но если кто-то хочет просмотреть весь источник, вы можете найти его в https://github.com/onmach/audio-sniffer.

Это было полезно?

Решение

Комбинаторрент парень здесь :)

Если память служит, проблема с ATTOPARSEC заключается в том, что требует ввода немного за один раз, наращивая ленивый сборник, который наконец-то объединяется. Мое «решение» было катить функцию ввода самостоятельно. То есть я получаю входной поток для ATTOPARSEC из сетевого сокета, и я знаю, сколько байтов ожидать в сообщении. По сути, я разделился на два случая:

  • Сообщение мало: читайте до 4k из розетки и емся, что ByteString немного немного за раз (ломтики ByteStrings быстро, и мы выбросим 4K после того, как он был исчерпан).

  • Сообщение «большой» (большой здесь означает около 16 килобайт в BitTorrent Peping): мы рассчитываем, сколько у нас есть 4K, который у нас может выполнить, а затем мы просто просим базовую сетевую розетку, чтобы заполнить вещи. У нас есть два поля, оставшаяся часть 4K кусок и большой кусок. У них есть все данные, поэтому объединяя их и разбирают их в том, что мы делаем.

    Вы можете оптимизировать Concatenation STAP.

TL; DR версия: Я обрабатываю его снаружи attoparsec и рубль петля, чтобы избежать проблемы.

Соответствующий комбинаторный коммит является FC131FE24, см.

https://github.com/jlouis/combinatorrent/commit/fc131fe24207909dd980c674aae6aaba27b966d4.

для деталей.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top