-
Notifications
You must be signed in to change notification settings - Fork 158
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow list fusion for Text and Text.Lazy unpack #629
Conversation
foldrText :: (Char -> b -> b) -> b -> Text -> b | ||
foldrText f z (Text arr off len) = go off | ||
where | ||
go !i | ||
| i >= off + len = [] | ||
| otherwise = let !(Iter c l) = iterArray arr i in c : go (i + l) | ||
{-# INLINE [1] unpack #-} | ||
| i >= off + len = z | ||
| otherwise = let !(Iter c l) = iterArray arr i in f c (go (i + l)) | ||
{-# INLINE foldrText #-} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note: This can, and maybe should, use Data.Text.foldr. But it was already here, so I didn't change that.
src/Data/Text/Lazy.hs
Outdated
-- * If it fuses: In phase 0, `foldrFB` inlines and `foldr` inlines. GHC | ||
-- optimizes the fused code. | ||
{-# RULES | ||
"Text.Lazy.unpack" [~1] forall t. unpack t = Exts.build (\c n -> foldrFB c n t) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"Text.Lazy.unpack" [~1] forall t. unpack t = Exts.build (\c n -> foldrFB c n t) | |
"Text.Lazy.unpack" [~1] forall t. unpack t = Exts.build (\cons nil -> foldrFB cons nil t) |
and same for the strict Text
. Fusion is cryptic enough, let's give an extra hint to readers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. I went with lcons
and lnil
because GHC warns about cons
shadowing the top-level cons
.
* Make Data.Text.unpack and Data.Text.Lazy.unpack good producers in list fusion. This allows them to fuse with good consumers of lists. Rewrite-back rules are included since the function bodies are large and we don't want to inline them if fusion doesn't occur. * For Data.Text.Lazy, this change means that `unpack`, which uses `unstreamList`, no longer fuses with `streamList` under Text's stream fusion framework. This scenario seems very unlikely, since nothing else must be done to the list in between the two functions. Even `pack . unpack` does not satisfy this rule. So we are not losing anything valuable here. * Add benchmarks for unpack, fusion and no fusion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks!
(I'm not a terribly big fan of rewrite rules in general because of their brittleness, but there does not seem to be any downside to this particular change)
unpack
, which usesunstreamList
, no longer fuses withstreamList
under Text's stream fusion framework. This scenario seems very unlikely, since nothing else must be done to the list in between the two functions. Evenpack . unpack
does not satisfy this rule. So we are not losing anything valuable here.Closes #628.
Benchmarks with GHC 9.10.1
Before
Show
After: