LINUX.ORG.RU

Распарсить большой XML на хаскеле

 


2

3

Пример большого XML-файла (~100 MiB):

wget -O - "http://mirror.yandex.ru/fedora/linux/releases/21/Everything/x86_64/os/repodata/e2a28baab2ea4632fad93f9f28144cda3458190888fdf7f2acc9bc289f397e96-primary.xml.gz" | gunzip >test-primary.xml
Код с использованием xml-conduit (практически копипаста из документации):
#!/usr/bin/runhaskell

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PackageImports #-}

import Prelude hiding (readFile, writeFile)
import "xml-conduit" Text.XML
import "xml-conduit" Text.XML.Cursor

main :: IO ()
main = do
    doc <- readFile def "test-primary.xml"
    let cursor = fromDocument doc
    mapM_ print $ cursor $/ laxElement "package" &/ laxElement "name" &/ content
Результат:
$ /usr/bin/time ./test >/dev/null
22.12user 1.24system 0:23.38elapsed 99%CPU (0avgtext+0avgdata 3906128maxresident)k
0inputs+0outputs (0major+975026minor)pagefaults 0swaps
Съело четыре гига на ровном месте. Как это починить?

Deleted

100 Мб для xml файла. Ну это же надо быть больным!

anonymous
()

C xml-conduit не работал, но на правах предположения: в описании Text.XML.Cursor говорится:

A Cursor represents a node in the DOM. It also contains information on the node's location.

DOM мне намекает на полную загрузку документа в память. Отсюда и 4Гб.

Sectoid ★★★★★
()
Ответ на: комментарий от quantum-troll

Какой конкретно ответ мне смотреть? Я не использую String (xml-conduit тоже его не использует), так что очевидно не первый.

Deleted
()

ты юзаешь DOM, а нужно SAX или какой-то другой streaming парсер

stevejobs ★★★★☆
()
Ответ на: комментарий от anonymous

Не только. Ещё для Фибоначчи и Аккермана.

anonymous
()

runhaskell - это же обёртка над ghci, не? Попробуй таки скомпилировать свой код в бинарник сначала.

hateyoufeel ★★★★★
()
Ответ на: комментарий от hateyoufeel

runhaskell - это же обёртка над ghci, не? Попробуй таки скомпилировать свой код в бинарник сначала.

То же самое получается.

Deleted
()
Ответ на: комментарий от hateyoufeel

Я уменьшил решение по ссылке до кода, который ничего не делает:

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PackageImports #-}

import Prelude hiding (readFile, writeFile)
import "xml-conduit" Text.XML
import "xml-conduit" Text.XML.Cursor

main :: IO ()
main = do
    doc <- readFile def "test-primary.xml"
    mapM_ print $ fromDocument doc $/ \_ -> []::[String]
Результат - все те же четыре гига памяти.

Deleted
()
Ответ на: комментарий от Deleted

С Text.XML.Stream.Parse у меня не получается заигнорить ненужные мне теги. На гитхабе есть заброшенный pull request на эту тему, он добавляет кучу какого-то кода, на понимание которого у меня удёт неделя. Вот ещё полезная ссылка в тему: http://stackoverflow.com/questions/21367423/streaming-xml-conduit-parse-results.

P.S. Вот уже два дня я грызу кактус (не только колючий, но и гранитный, судя по всему), без всякого полезного результата. Примеры из документации работают, какие-то простейшие случаи я могу придумать и изобразить сам, но при попытке сделать что-то полезное неизбежно получается epic fail. Короче, бросаю это гиблое дело во второй раз.

Deleted
()
Ответ на: комментарий от Deleted

Вот уже два дня я грызу кактус (не только колючий, но и гранитный, судя по всему), без всякого полезного результата.

Ты не понял, хацкель не предназначен для этого, он работает по-другому.

Oxdeadbeef ★★★
()
Ответ на: комментарий от Oxdeadbeef

У меня есть подозрение, что я сильно продвинутую библиотеку выбрал (xml-conduit). Быть может сегодня (когда просплюсь) попробую hexpat, вроде там нет супер-мега-труЪ-адвансед-концепций, а значит есть надежда на применимость к моему юзкейзу...

Deleted
()

Пускай полежит здесь для истории. Решил копнуть, откуда взялся мем про «работает по-другому». Похоже, что из этого сообщения archimag. Горе-лиспера потыкали мордой в то, что работа с command line в принципе отсутствует в стандарте Common LISP. Наш герой невозмутимо ответил:

Потому что программы на CL почти никогда этим не занимаются, их используют по другому.

Я, конечно, с лиспа фигею. Фейлостандарт какой-то, командная строка — это так, семечки. Обычного TCP/IP нетворкинга нету. Тредов нету, каждый городит свои костыли. Графики/звука нету, криптографии нету, нихуя нету. Про SQL я вообще не говорю. Такое впечатление, что LISP-программа работает в каком-то своем манямирке. А в Haskell такая же ситуация, интересно?

anonymous
()

Почему установили в теме флаг «решено»? Нашли решение? Если нашли, то напишите, пожалуйста в теме. Спасибо :)

amazpyel ★★★
()
Ответ на: комментарий от anonymous

Обычного TCP/IP нетворкинга нету. Тредов нету, каждый городит свои костыли. Графики/звука нету, криптографии нету, нихуя нету. Про SQL я вообще не говорю.

Ты это серьезно? Зачем это тащить в _стандарт_языка_? Для этого есть библиотеки. лол.

Oxdeadbeef ★★★
()
Ответ на: комментарий от Oxdeadbeef

Зачем ты передергиваешь? Никто не говорил про «стандарт языка». Common LISP = язык + стандартная библиотека, о последней и шла речь, если ты не понял.

А вот и спецификация. Почитай, кстати. Ты не найдешь там ни графического тулкита, ни обработки изображений, ни сети, ни неблокирующегося асинхронного ввода-вывода, ни remoting, ни криптографии, ни SQL, ни работы с текстом, ни работы с архивами, ни многопоточности, ни многопроцессности, ни работы с OS, ни потокобезопасных структур данных, ни monitoring & management, ни поддержки печати, ни работы со звуком и прочими медиа, ни работы с XML/JSON.

Но ведь это все ненужно, правда? Ведь в ЛИСПе есть анафорические лямбды, пандорический захват и кластеры метапарадигм.

anonymous
()
Ответ на: комментарий от amazpyel

Почему установили в теме флаг «решено»? Нашли решение? Если нашли, то напишите

Нашли.

Использовать нормальный язык, а не академическую поделку для вычисления факториала двадцатью способами.

anonymous
()
Ответ на: комментарий от anonymous

Но ведь это все ненужно, правда?

Ты не понял, лишп не предназначен для этого, он работает по-другому.

Oxdeadbeef ★★★
()
Ответ на: комментарий от anonymous

Пожалуйста, перестаньте мешать в кучу Common LISP и LISP. Дедушке Лиспу давно исполнилось 60.

Common - всего лишь один из многих диалектов (на мой взгляд устаревший ещё до завершения разработки, не очень удачный, но зато весьма разрекламмированный)

anonymous
()
Ответ на: комментарий от anonymous

TCP/IP нетворкинга нету. Тредов нету, каждый городит свои костыли. Графики/звука нету, криптографии нету, нихуя нету. Про SQL я вообще не говорю

Ну а че ты хочешь, полвека назад не было ничего из этого. Ну застряли пацаны в 60-ых, че ты к ним цепляешься-то.

А тред фееричный, почитал и прослезился, образцовый лиспосрач. Какое время было, Саныч, Кука, Архимаг, где они все.

anonymous
()
Ответ на: комментарий от SystemD-hater

В стандарте C есть графический тулкит?

В стандарте POSIX есть сеть, async I/O, multithreading, multiprocessing и работа с OS.
В стандарте C++ все вышеописанное плюс алгоритмы и структуры данных.
В Python есть все вышеописанное плюс L10N, архивы и XML/JSON.
В Java есть все вышеописанное плюс графический тулкит, обработка изображений, remoting, криптография, SQL, потокобезопасные структуры данных, monitoring & management, поддержка печати, работа со звуком и прочими медиа.

В стандарте же LISP'а ничего этого нет.

anonymous
()
Ответ на: комментарий от anonymous

В стандарте POSIX есть сеть, async I/O, multithreading, multiprocessing и работа с OS

Никогда до этого не слышал о языке программирования POSIX.

В Python есть все вышеописанное плюс L10N, архивы и XML/JSON.

Можно номер стандарта ISO/ANSI этого Путшон?

В Java

Неудивительно, язык пилится монопольно одной компанией.

В стандарте же LISP'а ничего этого нет.

Там много чего ещё нет.

SystemD-hater
()
Ответ на: комментарий от SystemD-hater

Можно номер стандарта ISO/ANSI этого Путшон?

А можно стандарт ГОСТ этого Лишп?

Там много чего ещё нет.

Вот, в частности, поэтому лишп и находится сейчас там, где находится, — на свалке истории.

anonymous
()
Ответ на: комментарий от anonymous

C++ — в него всегда тянули всё говно, что вокруг видели. Так что он не релевантен.

Итого, анонимный петушок, ты не назвал ни одного языка (выяснилось, что ты даже не отличаешь ЯП от интерфейса ОС), стандартизованного ANSI или ISO, в котором было бы всё, от графического тулкита и работы с печатью до всех известных человечеству структур данных.

Так в чём был поинт наезда на CL?

SystemD-hater
()
Ответ на: комментарий от anonymous

А можно стандарт ГОСТ этого Лишп?

Ты таким образом признаёшь, что обосрался, приведя Python и Java в пример стандартизированных языков?

SystemD-hater
()

Не совсем честно, но покатит

{-# LANGUAGE OverloadedStrings #-}

import Control.Applicative
import Text.HTML.TagSoup
import qualified Data.ByteString.Lazy.Char8 as LBS

main = do
    dat_ <- LBS.readFile "test-primary.xml"
    go (parseTags dat_)
    where
        go [] = return ()
        go xs = go2 $ dropWhile (isTagOpenName "package") xs

        go2 [] = return ()
        go2 xs = go3 $ dropWhile (not . liftA2 (||) (isTagCloseName "package") (isTagOpenName "name")) xs

        go3 [] = return ()
        go3 (TagClose "package":xs) = go xs
        go3 (TagOpen "name" _:TagText t:xs)  = LBS.putStrLn t >> go2 xs
        Command being timed: "./test2"
        User time (seconds): 23.23
        System time (seconds): 0.36
        Percent of CPU this job got: 99%
        Elapsed (wall clock) time (h:mm:ss or m:ss): 0:23.75
        Average shared text size (kbytes): 0
        Average unshared data size (kbytes): 0
        Average stack size (kbytes): 0
        Average total size (kbytes): 0
        Maximum resident set size (kbytes): 6604
        Average resident set size (kbytes): 0
        Major (requiring I/O) page faults: 0
        Minor (reclaiming a frame) page faults: 821
        Voluntary context switches: 1
        Involuntary context switches: 88457
        Swaps: 0
        File system inputs: 0
        File system outputs: 0
        Socket messages sent: 0
        Socket messages received: 0
        Signals delivered: 0
        Page size (bytes): 4096
        Exit status: 0
qnikst ★★★★★
()
Ответ на: комментарий от qnikst

Я вот, что не понял. Почему ~100M xml выела ~4g памяти? Мне кажется, это не нормально, даже не смотря на то, что в ОП посте xml полностью парсится в dom (судя по доке). Хотел на выходных попробовать разобраться, но всё же?

anonymous
()
Ответ на: комментарий от SystemD-hater

Специалист: (приводит аргументированное мнение)
Лиспер: ЛОЛ ГОВНО ПЕТУШОК ОБОСРАЛСЯ!!!111
Специалист: (пожимает плечами)

anonymous
()
Ответ на: комментарий от anonymous

Вот выхлоп статистики из проги ТС.

  47,021,872,456 bytes allocated in the heap
   6,985,195,080 bytes copied during GC
   1,611,906,656 bytes maximum residency (15 sample(s))
      23,743,352 bytes maximum slop
            3860 MB total memory in use (0 MB lost due to fragmentation)

                                    Tot time (elapsed)  Avg pause  Max pause
  Gen  0     89481 colls,     0 par    5.57s    5.57s     0.0001s    0.0019s
  Gen  1        15 colls,     0 par    6.10s    6.12s     0.4078s    2.4977s

  INIT    time    0.00s  (  0.00s elapsed)
  MUT     time   13.95s  ( 14.02s elapsed)
  GC      time   11.68s  ( 11.68s elapsed)
  EXIT    time    0.26s  (  0.28s elapsed)
  Total   time   25.90s  ( 25.98s elapsed)

  %GC     time      45.1%  (45.0% elapsed)

  Alloc rate    3,369,564,687 bytes per MUT second

  Productivity  54.9% of total user, 54.7% of total elapsed

Из него видно, что реально макс занятое место это 1.6G, причем на убоку потрачено аж половина времени.. В общем что там происходит мне судить сложно, т.к. в потроха снойманобиблиотек лезть совсем не хочется, подозреваю, что когда он ходит курсором по документу он перестраивает его кучу раз не самым эффективным способом.

Если охота красивого API и DOM не пугает, то я бы предложил взглянуть на xml-lens.

qnikst ★★★★★
()
Ответ на: комментарий от qnikst

чота как-то печально все с xml-lens, ну и ладно

qnikst ★★★★★
()

Сорри за оффтоп, на джаве со стандартной библиотекой за 4.4 секунды парсит с ограничением в 600 мегабайтов (если чуть больше дать, чтобы GC не сильно мешал, за 3 секунды парсит). Может она больше подойдёт для такой задачи? Если не использовать namespace то ещё быстрее работает (примерно в полтора раза).

Есть предположение, что на C с libxml будет ещё быстрее и ещё меньше памяти кушать. Можно к хаскелю присобачить биндинги (или найти готовые), чтобы основной работой код на C занимался.

Legioner ★★★★★
()
Последнее исправление: Legioner (всего исправлений: 1)
Ответ на: комментарий от qnikst

Если честно, эту запись не читал, просто на работе запустил под win (cygwin на самом деле). И посмотрел на пик комунизма в tskMng, примерно растянувшийся на 4gb. Но даже с 1.6G, мне кажется, перебор.

anonymous
()
Ответ на: комментарий от Legioner

Сорри за оффтоп, на джаве со стандартной библиотекой за 4.4 секунды парсит с ограничением в 600 мегабайтов (если чуть больше дать, чтобы GC не сильно мешал, за 3 секунды парсит). Может она больше подойдёт для такой задачи? Если не использовать namespace то ещё быстрее работает (примерно в полтора раза).

парсит и выводит все на экран?

qnikst ★★★★★
()
Ответ на: комментарий от anonymous

ну там когда представлени dom делается дочерта лишних структур создается, а как он внутрях парсит я даже не представляю. Вообще дом парсеры для xml по сотне мегабайт и в других языках в такой же ад превратятся.

qnikst ★★★★★
()
Ответ на: комментарий от qnikst

Парсит в DOM. После этого всё лежит в оперативной памяти, там уже делай что хочешь, к парсингу и XML это уже отношения не имеет.

Legioner ★★★★★
()
Ответ на: комментарий от Deleted

ну я строго привязался к структуре документа, поидее в go3 я не сразу должен выдавать первый Text, а должен считывать все Text до завершения тега, а затем возвращаться на go2. Но в силу того, что в name только текст и того, что TagSoup объединяет текстовые события в одно (по умолчанию) все работает.

qnikst ★★★★★
()
Ответ на: комментарий от Legioner

так и здесь парсинг и построение DOM занимает минимальное время по сравнению с поиском по нему и выводом [1]. Так что получается предложение поменять шило на мыло.

[1] в принципе если интересно можно это даже повыводить через traceEvent

qnikst ★★★★★
()
Ответ на: комментарий от qnikst

Я может чего не понимаю, но через XPath прекрасно ищет и выводит (правда память жрёт, до полутора пришлось докинуть). Если ручками написать, вообще ничего жрать не будет и работать будет моментально, всё же в памяти висит в массивах и классах.

Legioner ★★★★★
()
Ответ на: комментарий от Legioner

Собственно проверил, за полсекунды перебирает все name-ы практически без расхода памяти. Код конечно не хаскель, но там больше проблема в идиотском W3C API, чем в джаве, был бы нормальный красивый API, было бы не хуже.

Legioner ★★★★★
()
Ответ на: комментарий от crowbar

можете, пожалуйста, подробно объяснить связь SAX-парсинга и чистоты. А так же пояснить невозможность SAX-парсинга в Haskell, особенно при учете того, что решение на основе SAX-парсера было продемонстрировано в данном треде.

qnikst ★★★★★
()
Ответ на: комментарий от Legioner

Я может чего не понимаю, но через XPath прекрасно ищет и выводит (правда память жрёт, до полутора пришлось докинуть).

ну у ТС тоже ищет и выводит, и правда жрет под 4Г если не тюнить параметры GC (что следовало бы), при этом есть подозрение (судя по документации), что при хождении курсором по структуре генерится лишний мусор. При использовании более подходящего в этом случае SAX парсера (даже при использовании list, а не хороших структур данных, и первого попавшегося парсера) поведение очень отличается (выхлоп \time и код для того, чтобы проверить самому и сравнить приведен выше).

Если ручками написать, вообще ничего жрать не будет и работать будет моментально, всё же в памяти висит в массивах и классах.

можете использовать более точные определения того, как и что писать, чтобы было понятно о чем вы пишите? :)

qnikst ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.