Closed byorgey closed 7 years ago
I will add a function that uses a lookup to prevent this
I'm not sure I understand. How exactly will doing a lookup prevent this?
haskell-chart already uses a lookup for custom fonts. The default fonts on the other hand have no lookup and the compiler most likely does not recognize constant assignement in a do notation of a paramerterized function. To prevent a mistake like this it is convenient to add a global Hashmap that caches the Font data, although it might add a little overhead.
Hmm, I see. It is certainly worth trying.
Hi; this is still around and makes performance honestly not acceptable in any application using textSVG
multiple times, sadly. Is there any plan to fix it? I may give it a go shortly but if there's a patch lying around somewhere I'd rather spare myself the pain.
Hm, upon testing, seems loading only happens once. Perhaps something else should be blame for poor performance now.
main :: IO ()
main = renderSVG "/tmp/rendered.svg" (dims2D 500 500) $
vcat $ flip map [1 .. 10] (\i -> stroke $ textSVG' textOpts ("STRING " ++ show i))
textOpts :: TextOpts Double
textOpts = TextOpts
{ textFont = lin
, mode = INSIDE_H
, spacing = KERN
, underline = False
, textWidth = 20
, textHeight = 20 }
COST CENTRE MODULE %time %alloc
>>=.\ Data.Attoparsec.Internal.Types 23.0 15.8
>>=.\.succ' Data.Attoparsec.Internal.Types 18.3 9.0
>>= Data.Attoparsec.Internal.Types 6.1 0.0
plus.\ Data.Attoparsec.Internal.Types 4.4 7.5
fmap.\ Data.Attoparsec.Internal.Types 4.1 4.1
fmap.\.succ' Data.Attoparsec.Internal.Types 3.5 7.0
breakn Text.XML.Light.Lexer 3.2 5.8
decode_text.(...) Text.XML.Light.Lexer 2.9 7.7
plus.\.lose' Data.Attoparsec.Internal.Types 2.6 0.0
breakn.(...) Text.XML.Light.Lexer 2.5 8.5
linenumber Text.XML.Light.Lexer 1.9 9.3
plus Data.Attoparsec.Internal.Types 1.7 0.0
pure.\ Data.Attoparsec.Internal.Types 1.6 0.0
*> Data.Attoparsec.Internal.Types 1.6 1.4
myfloat Graphics.SVGFonts.ReadPath 1.3 0.2
atParam Diagrams.Segment 1.2 1.7
decimal.step Data.Attoparsec.ByteString.Char8 1.0 0.8
pathFromString Graphics.SVGFonts.ReadPath 1.0 0.1
pathElement Graphics.SVGFonts.ReadPath 1.0 0.3
loadFont Graphics.SVGFonts.ReadFont 0.9 2.8
decode_attr Text.XML.Light.Lexer 0.7 3.3
uncons Text.XML.Light.Lexer 0.4 2.7
I suppose this can be closed in favour of "parser sucks" ticket or something of sorts
@Fuuzetsu thanks for the nudge on this issue. Can you be more specific about the poor performance you are seeing? If the parser is only being called once then it should not make a difference how many times you use textSVG
.
I think there are few problems:
Initial load overhead is too high: if we want to render few pieces of text, it sucks that loading the font takes so long; better loading qould help here. I managed to speed up parser by third pretty trivially on a branch somewhere
Following up on above, memory cost seems way too high for what we're doing, we're reading 18MB file in effectively a flat format so why does it take so 100+MB resident memory to load the font? This sucks for programs where memory cost would otherwise be low.
Actual usage seems to cost a lot. Even for simple, short strings it takes a lot of time and memory to create a Diagram. If you have multiple strings then things get really bad. For examppe I was able to reduce memory usage and runtime by around an order of magnitude just by reducing number of times we call textSVG. I have to measure if maybe it's worth rendering 1 big string at once where possible. For comparison, using text from diagrams instead of textSVG nearly completely eliminates any runtime and memory issuea from ghc-events-anaylze whereas it qould happily take many many minutes and tens of GBs of memory even on small input.
I can maybe fix 1 and 2 reducing constant overhead but really I'm interested in 3. Honestly I just want some fast text, not even vectorised, that is sized as far as diagrams is concerned. My current hack is to take longest string from my set, textSVG and take width as an approximation for getting biggest "width". As far as addressing core problem (textSVG is slow and memory hungry), I know very little about diagrams and don't know if we can have much improvement there...
I think this issue can be closed if there are issues for above points as I managed to convince myself that only 1 load is occuring.
EDIT: I think I managed to drastically improve the situation and will send PR shortly, just cleaning… EDIT2: It runs better but not for the right reason and not by a factor I thought it was so holding off on that PR…
Re: your edit, great! I'm sure (1) and (2) could be improved. As for (3), it may be more an issue with diagrams itself than with SVGFonts. The diagrams text
function creates a simple text object which uses native text support in the backend. SVGFonts, on the other hand, creates (possibly complicated) paths. Empirically we are talking about ~20 Bezier segments per letter. I don't actually think diagrams should be that slow in handling paths with hundreds or thousands of segments but I am not sure.
OK, I'm closing this issue for now then. I've opened #22 as a placeholder for further performance improvements.
It seems that fonts are sometimes repeatedly reloaded from disk, once for each call to
textSVG
or a similar function. See the previous discussion at https://github.com/timbod7/haskell-chart/pull/83 .