XML.Light データ型の効率的な XML レンダーを作成しようとしていますData.Text.Lazy.Builder
。ただし、ソリューションからパフォーマンスを引き出すのに苦労しています。
{-# LANGUAGE OverloadedStrings #-}
import Data.Text (Text, unpack)
import Text.XML.Light
import qualified Data.Text.Lazy as LT
import qualified Data.Text.Lazy.Builder as LB
import Data.Foldable (foldMap)
import Data.Monoid (mconcat)
data Tag = Tag !Text
data Artist = Artist { artistName :: !Text , artistTags :: ![Tag] }
class ToXML a where toXML :: a -> Content
instance ToXML Artist where
toXML a = Elem $
Element (unqual "artist") []
[ text (artistName a)
, Elem $ Element (unqual "tag-list") []
(map toXML (artistTags a))
Nothing
]
Nothing
instance ToXML Tag where
toXML (Tag t) = Elem $ Element (unqual "tag") [] [ text t ] Nothing
text :: Text -> Content
text t = Text $ CData CDataText (unpack t) Nothing
render :: Content -> LB.Builder
render (Elem e) = renderElement e
render (Text s) = LB.fromString (cdData s)
renderElement :: Element -> LB.Builder
renderElement element = mconcat
[ LB.singleton '<'
, LB.fromString . qName . elName $ element
, LB.singleton '>'
, foldMap render (elContent element)
, LB.fromText "</"
, LB.fromString . qName .elName $ element
, LB.singleton '>'
]
main :: IO ()
main = let artist = Artist "Nirvana" (replicate 5000000 (Tag "Hi"))
xml = Element (unqual "metadata") [] [ toXML artist ] Nothing
in print (LT.length . LB.toLazyText . renderElement $ xml)
によると+RTS -s
:
7,368,153,472 bytes allocated in the heap
2,625,983,944 bytes copied during GC
708,149,024 bytes maximum residency (13 sample(s))
21,954,496 bytes maximum slop
1443 MB total memory in use (0 MB lost due to fragmentation)
Tot time (elapsed) Avg pause Max pause
Gen 0 14270 colls, 0 par 1.65s 1.69s 0.0001s 0.0009s
Gen 1 13 colls, 0 par 2.57s 2.80s 0.2157s 1.2388s
TASKS: 3 (1 bound, 2 peak workers (2 total), using -N1)
SPARKS: 0 (0 converted, 0 overflowed, 0 dud, 0 GC'd, 0 fizzled)
INIT time 0.00s ( 0.00s elapsed)
MUT time 1.81s ( 1.84s elapsed)
GC time 4.22s ( 4.50s elapsed)
EXIT time 0.07s ( 0.09s elapsed)
Total time 6.11s ( 6.43s elapsed)
Alloc rate 4,064,658,288 bytes per MUT second
Productivity 30.8% of total user, 29.3% of total elapsed
これはひどいです。生産性がどん底なだけでなく、64MB の XML をレンダリングするために 7GiB 以上がヒープに割り当てられています。それは非常に非効率的です!しかし、このゴミのすべてが実際にどこから来ているのか、私にはわかりません。でヒープ プロファイルを生成し、次の+RTS -p
ようにレンダリングしましたhp2ps
。
また+RTS -l
、これを ThreadScope で実行してレンダリングしました。
悲しいことに、現在どのようにすればよいかわかりませんが、これらのピースを組み合わせて、この生産性を上げ、メモリ使用量を下げることです。XML.Light
の型が最適ではない(厳密性がない、String
over )かどうか疑問に思いますText
が、それでも-これは遅いですか?
私はまた、私が少し奇妙に感じる他の何かを観察しました. 私が変更main
した場合:
main :: IO ()
main = let artist = Artist "Nirvana" (replicate 5000000 (Tag "Hi"))
xml = Element (unqual "metadata") [] [ toXML artist ] Nothing
in print (LT.length $ LB.toLazyText $ mconcat $ map (render.toXML) $ artistTags artist)
生産性は 94% まで跳ね上がります。おそらく、toXML
問題があり、過度に怠惰であるという点で、再帰に関係している可能性があります。