Skip to content

Improve render performance#29

Merged
seanhess merged 1 commit into
seanhess:mainfrom
kschweppe:main
May 14, 2026
Merged

Improve render performance#29
seanhess merged 1 commit into
seanhess:mainfrom
kschweppe:main

Conversation

@kschweppe
Copy link
Copy Markdown
Contributor

I noticed renderLines is causing a significant performance bottleneck when rendering large tables. This can easily be addressed by using Builder instead of Text.

Another small improvement is to construct Line directly with the correct indent to avoid unnecessary allocations caused by calling addIndent, which is called quite often.

Before:

COST CENTRE       MODULE                               SRC                                                             %time %alloc

renderLines       Web.Atomic.Render                    src/Web/Atomic/Render.hs:(177,1)-(187,30)                        80.2   98.8
parseEntityValues Database.Persist.Sql.Util            Database/Persist/Sql/Util.hs:(168,1)-(194,51)                     5.1    0.5
rawSelectSource   Database.Esqueleto.Internal.Internal src/Database/Esqueleto/Internal/Internal.hs:(3064,1)-(3080,40)    2.8    0.2
elementLines      Web.Atomic.Render                    src/Web/Atomic/Render.hs:(54,1)-(78,27)                           1.2    0.1

After:

COST CENTRE       MODULE                               SRC                                                             %time %alloc

parseEntityValues Database.Persist.Sql.Util            Database/Persist/Sql/Util.hs:(168,1)-(194,51)                    37.6   44.6
rawSelectSource   Database.Esqueleto.Internal.Internal src/Database/Esqueleto/Internal/Internal.hs:(3064,1)-(3080,40)   18.9   21.5
unsafeEff         Effectful.Internal.Monad             src/Effectful/Internal/Monad.hs:150:1-29                          4.1    0.8
elementLines      Web.Atomic.Render                    src/Web/Atomic/Render.hs:(60,1)-(82,79)                           3.7    4.0
column            Database.Sqlite                      Database/Sqlite.hs:(546,1)-(561,36)                               2.9    0.4
text              HTMLEntities.Text                    library/HTMLEntities/Text.hs:(24,1)-(25,21)                       2.5    3.5
fuse              Data.Conduit.Internal.Conduit        src/Data/Conduit/Internal/Conduit.hs:769:1-12                     2.1    1.3
stepConn          Database.Sqlite                      Database/Sqlite.hs:(331,1)-(336,46)                               1.8    0.0
renderLines       Web.Atomic.Render                    src/Web/Atomic/Render.hs:(178,1)-(188,44)                         1.6    2.0
tcol              Web.Hyperbole.View.Tag               src/Web/Hyperbole/View/Tag.hs:(224,1)-(225,69)                    1.1    0.3
char              HTMLEntities.Text                    library/HTMLEntities/Text.hs:(9,1)-(16,25)                        1.0    1.7
nodesLines        Web.Atomic.Render                    src/Web/Atomic/Render.hs:50:1-48                                  0.6    2.0

@seanhess
Copy link
Copy Markdown
Owner

This is awesome, thanks!

@seanhess
Copy link
Copy Markdown
Owner

@kschweppe could you update RenderSpec to match the way the new rendering works? It's currently calling addIndent in a couple tests.

Also main is now passing CI if you want to merge

@kschweppe
Copy link
Copy Markdown
Contributor Author

Ah I missed the test. I just added addIndent again, or do you prefer to drop it and update the tests? Also the indent argument works different now, previously htmlLines 0 would mean no indentation, and now it always increments with a fixed step 2. Is this okay? Otherwise we could add an additional argument to have the same meaning together with the new implementation

elementLines :: Int -> Int -> Element -> [Line]
elementLines indent depth elm = ...

@seanhess
Copy link
Copy Markdown
Owner

We can remove addIndent, I just want to make sure the output is indenting correctly, so the tests would need to be updated.

@seanhess
Copy link
Copy Markdown
Owner

Also the indent argument works different now, previously htmlLines 0 would mean no indentation, and now it always increments with a fixed step 2. Is this okay? Otherwise we could add an additional argument to have the same meaning together with the new implementation

I don't mind if the argument meaning changes, as long as the output is properly indented.

* Use Builder for concatenation
* Avoid unnecessary allocations for indentation
@kschweppe
Copy link
Copy Markdown
Contributor Author

Okay, should be good now.

Btw, I needed to add build-tool-depends: skeletest:skeletest-preprocessor to the cabal file, otherwise I would get ghc-9.10.3: Haskell pre-processor: could not execute: skeletest-preprocessor.

@seanhess seanhess merged commit 8e9ab91 into seanhess:main May 14, 2026
2 checks passed
@seanhess
Copy link
Copy Markdown
Owner

Can't wait to get this into a release! Nice work!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants