Elmstatic v0.7.0 has substantially improved performance as it generates HTML pages in parallel.
The largest performance bottleneck in Elmstatic was that it generated pages sequentially. To generate HTML, Elmstatic executes the JavaScript produced by Elm. As this is a CPU-intensive process, there would be little advantage in executing page generation functions asynchronously via promises, and I used synchronous functions for everything.
However, Node provides also the ability to run computations in worker threads, and that’s useful for running CPU-intensive work in parallel. Generating pages happens to be very amenable to be done in parallel, as each page is generated independently, so I decided to take advantage of that.
It wouldn’t make sense to create a thread per page. Instead, it would be good to have a pool of worker threads, with its size determined by the number of CPU cores.
Luckily, there’s an NPM package that does just that, workerpool
. It also conveniently allows me to pass a function to execute in a thread.
With this package, I need to create a thread pool, and then I can simply pass it a function and some arguments, and get a Promise
back:
const workerPool = WorkerPool.pool()
workerPool.exec(generateHtml, [elmJs, page])
.then((html) => /* write HTML to disk etc. */)
The function passed to the pool has to be serialisable.
Once the work is done, the pool needs to be terminated with workerPool.terminate()
.
Overall, the changes turned out to be minimal, although of course promises have made the code a bit more convoluted. But on the plus side, Elmstatic now generates sites quite a bit faster.