Valery Silaev's Blog

if it ain't broken we'll break it

It has been long since I’ve posted anything to this blog, and even longer since the my last post about Tascalate JavaFlow library… Meanwhile, the library is steadily progressing up to release 2.4.0 and there are some interesting stories I’d like to share.

First, Tascalate JavaFlow is used as an engine for my new Tascalate  Async / Await library. Yep, same async / await you can find in C# since 5.0 and in ECMAScript (pronounced as “JavaScript”) since ECMAScript 2017, and somewhat similar functionality in Kotlin. And it doesn’t stop with raw copying of features! Scheduling is taken seriously (better than in C#), cancellation is addressed (more convenient that in C#), even asynchronous generators (C# 8, ECMAScript 2018) are implemented! If you are new to the subject, please start your explorations with the description of the colored function problem — it’s an excellent explanation how async / await solves some of the issues of the asynchronous callbacks’ hell.

The Tascalate Async / Await library deserves its own series of articles, for now please read on the documentation available on the project home page and examples. What is relevant to this article is that the Tascalate Async / Await was started as a proof-of-concept that it’s possible to build something really useful with Tascalate JavaFlow and that API design was done right. And that there are no critical bugs left, for sure…

Obviously, there were numerous! Eric Sink in his book, Eric Sink on the Business of Software, wrote, “I like the smell of a freshly killed bug.” Though, I’m not a fan of Asian cuisine, I still remember not only the smell, but as well the taste of all that bugs discovered on the Tascalate JavaFlow release path from the version 2.0 through 2.4! And now I’m happy to announce that Tascalate Async / Await is pretty alive and well… erghhh… that Tascalate JavaFlow is mostly free from critical issues and can be used in development! So “eat your own dog food” motto proved to be true once again.

Once I get confident with a quality of my library, I were ready to address performance issues… Well, I’m lying now. What I started to do is searching is anyone around is using Tascalate JavaFlow and what is a feedback (hoping to find a positive one). So I start to google whether or not my library is popular enough. And I found a pretty interesting document that surprised me badly — Effect Handlers for the Masses by Jonathan Brachthäuser, Philipp Schuster, and Klaus Ostermann, where my library was compared against authors’ own implementation of continuations as well as numerous alternative continuations / coroutines libraries. Well, being a worst example out of five is somewhat disappointing… Being 7 times slower than the closest competitor is even worse! So I’ve contacted the author for tests used, and get back to the Tascalate JavaFlow runtime that behaves thaaaat poorly. Pretty fast, two changes was made to the code:

  1. When porting code from Apache Commons JavaFlow I totally overlooked performance issues inside org.apache.commons.javaflow.core.Stack class. There were separate stacks for each primitive type — int (covering byte, char, boolean), long, double, float. Combining them all into one stack significantly improves performance! Next, there were unconditional debug statements that eats CPU cycles just for nothing but with excellent appetite! Addressing just this single issue gives a 250+% speedup!
  2. The Tascalate JavaFlow library used mutli-shot continuations only. They are thread-safe and may be resumed multiple times. However, for a lion share of possible usage scenarios this is an overkill. Single-shot single-thread continuations are what is enough! Hence the library has now an option to create either single-shot optimized Continuation-s or multi-shot Continuation-s depending on requirements. Changes are documented in generated JavaDoc-s, so please read the API docs for details.

Just to get you a rough picture what this performance optimization brings, here is a cite from my reply to the Jonathan Brachthäuser:

  1. Initial case (with expensive debug statements and multi-shot continuations):
    1043ms
  2. Keep expensive debug but use “optimized” single-resume continuation (no array copy)
    759ms
  3. Expensive calls to debug are removed but continuation is still multi-shot:
    260ms
  4. Both optimizations applied (expensive calls to debug are removed, single-resume continuations)
    124ms

So it’s almost 8.5 times faster than the original version! Even with multi-shot continuations it’s almost 4 times faster for the given tests due to fixes in the Stack class alone! All in all, the Tascalate JavaFlow performance should be on par with Quasar for the majority of cases.

Finally, the project was split on three parts — the Tascalate JavaFlow library itself, the Tascalate JavaFlow Extras extensions that uses Java 8+ API-s and examples. This should help evolving all of three separately with their own release cycle. And among examples you may find an example how the library can be used… drum roll.. in JEE project! For now it’s tested only with WildFly 9/10/11, but the support for GlassFish / Payara is in progress!

Leave a comment