Aplite: an EDSL for speeding up your Haste programs
As the talks from this year’s Haskell Symposium hit YouTube today, this might be a good time for a post about the Aplite EDSL for speeding up the most computationally intensive parts of your Haste programs. Of course, you could just watch the talk instead, or read the paper if you want a more in-depth treatment.
So what is Aplite?
The best part? These low-level fragments integrate seamlessly with the rest of your Haskell program, letting you mix and match high and low level code as needed by your application’s performance characteristics.
Aplite functions run in the
Aplite monad, which only supports a small set of low-level constructs:
- arithmetic, logic and bitwise operations;
- mutable references;
- arrays; and
Aplite by example
To get a feel for what the language looks like, the following short Aplite function multiplies two square matrices.
type Mat = IOUArray Word32 Double matMult :: Word32 → Mat → Mat → IO Mat matMult = aplite $ λsize m1 m2 out → do out <- newArr (size*size) for (0, 1, Excl size) $ λi → do for (0, 1, Excl size) $ λj → do sumRef ← initRef 0 for (0, 1, Excl size) $ λk → do x ← getArr m1 (i*size+k) y ← getArr m2 (k*size+j) modifyRef sumRef (+(x*y)) getRef sumRef >>= setArr out (i*size+j) return out
One thing here is especially noteworthy:
matMult is imported as a Haskell function in the IO monad, callable from any other Haskell code. This is due to the use of the
aplite function, the main interface to and workhorse of the Aplite language.
What’s cool about it?
Besides producing highly efficient code and allowing seamless integration between high and low level code, Aplite has a few other properties that are particularly neat. The Aplite compiler runs in the browser, at run time. This lets the Haskell host program consider the browser environment, user input and other factors, and then modify any Aplite functions before compiling them. This idea is known as multi-stage programming, and is generally used to specialize programs to a bunch of factors that are not known at compile time, allowing programs to run faster than without this specialization.
Developers tend to be busy people however, and might not have the time to sit around exploring cool ways to specialize their programs. To make their lives easier, Aplite takes this idea one step further and actually automates the specialization process. Using Aplite’s run time specializer, the developer only needs to provide a small benchmark for each function that they want to specialize. When the specializer is run, Aplite will recompile the function under specialization with all available backends. Each recompiled function is then used to run the user-provided benchmark, and the old, unspecialized version of the function is overwritten with the version that turns out to be the fastest.
Since the specializer can be triggered by the host program at till, an application might start off not using any specialization, realize that it is running too slowly, and attempt to speed up one or more Aplite functions using the specializer depending on the needs of the application. Put simply, Aplite gives developers more tools to speed up their code at run time.
Try it out!
You can download and try Aplite for your own use case today! Remember, however, that this is pretty new research, and so it’s rather rough around the edges.
For a more in-depth look at the language, the properties that make it cool, more examples and a bunch of benchmarks, have a look at the paper.