Performance 提高基于文本的XML渲染器的生产效率和性能
我正在尝试为XML.Light数据类型编写一个高效的XML呈现,我正在尝试使用Performance 提高基于文本的XML渲染器的生产效率和性能,performance,haskell,Performance,Haskell,我正在尝试为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
来实现这一点,因为这似乎是一个显而易见的选择。但是,我很难从我的解决方案中获得任何性能:
{-# 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)
这太可怕了。不仅是最低的生产率,堆中还分配了超过7GiB的内存来呈现64MB的XML。这看起来效率太低了!然而,我不知道这些垃圾到底是从哪里来的。我用+RTS-p
生成了一个堆配置文件,并用hp2ps
呈现它:
我还用+RTS-l
运行了它,并用ThreadScope渲染了它:
遗憾的是,我现在不知道该怎么做,就是把这些部分放在一起,提高生产率,降低内存使用率。我确实想知道XML.Light
中的类型是否不是最佳的(没有严格性,String
超过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
中递归有问题,而且过于懒惰。我解决了这个问题,我认为这是GHC中的一个缺陷
如果我们改变这一行:
, LB.fromString . qName . elName $ element
为此:
, LB.fromString $ qName . elName $ element
然后我们得到了我们期望的表现。似乎用qName
组合LB.fromString
可以防止一些内联,因此不会发生融合。我认为这真的很危险,所以我将把这个问题转移到GHCs bug tracker的bug报告中,看看那边的智者是怎么想的
说一句“抓住你了!” 你知道
artistTags::![Tag]
不太重要,是吗?只需查看列表是否为空即可。您可以尝试在Artist
实例(deepseq或其他)中强制执行(map-toXML(artistTags a))
。这可能会有帮助也可能会有伤害,现在还不知道foldMap render(elContent元素)
也可以有一部分,但是一般来说,text
是非常有效的,所以我不把它列为主要怀疑因素。
, LB.fromString $ qName . elName $ element