fusion-plugin-0.2.2: GHC plugin to make stream fusion more predictable.
Copyright(c) 2019 Composewell Technologies
LicenseApache-2.0
Maintainerstreamly@composewell.com
Stabilityexperimental
PortabilityGHC
Safe HaskellNone
LanguageHaskell2010

Fusion.Plugin

Description

Stream fusion depends on the GHC case-of-case transformations eliminating intermediate constructors. Case-of-case transformation in turn depends on inlining. During core-to-core transformations GHC may create several internal bindings (e.g. join points) which may not get inlined because their size is bigger than GHC's inlining threshold. Even though we know that after fusion the resulting code would be smaller and more efficient. The programmer cannot force inlining of these bindings as there is no way for the programmer to address these bindings at the source level because they are internal, generated during core-to-core transformations. As a result stream fusion fails unpredictably depending on whether GHC was able to inline the internal bindings or not.

See GHC ticket #17075 for more details.

This plugin provides the programmer with a way to annotate certain types using a custom Fuse annotation. The programmer would annotate the types that are to be eliminated by fusion via case-of-case transformations. During the simplifier phase the plugin goes through the relevant bindings and if one of these types are found inside a binding then that binding is marked to be inlined irrespective of the size.

At the right places, fusion can provide dramatic performance improvements (e.g. 10x) to the code.

Synopsis

Using the Plugin

This plugin was primarily motivated by fusion issues discovered in streamly but it can be used in general.

To use this plugin, add this package to your build-depends and pass the following to your ghc-options:

ghc-options: -O2 -fplugin=Fusion.Plugin

To dump the core after each core to core transformation, pass the following to your ghc-options:

ghc-options: -O2 -fplugin=Fusion.Plugin -fplugin-opt=Fusion.Plugin:dump-core

Output from each transformation is then printed in a different file.

Implementation Details

The plugin runs after the simplifier phase 0. It finds all non recursive join point bindings whose definition begins with a case match on a type that is annotated with Fuse. It then sets AlwaysInlinePragma on those bindings. This is followed by two runs of a gentle simplify pass that does both inlining and case-of-case. This is followed by the rest of CoreToDos.

Results

This plugin has been used extensively in the streaming library streamly. Several file IO benchmarks have shown 2x-6x improvements. With the use of this plugin stream fusion in streamly has become much more predictable which has been verified by inspecting the core generated by GHC and by inspection testing for the presence of the stream state constructors.

Orphan instances

Outputable Fuse Source # 
Instance details

Methods

ppr :: Fuse -> SDoc #

pprPrec :: Rational -> Fuse -> SDoc #