Imagine a world where your Instagram froze anytime you uploaded a photo to your page. Asynchronous programming allows an application to do more than one thing at a time. Async operations speed computation, from your browser to highest of high-frequency trading operations
Financial services company Hudson River Trading uses async functions as a preferred method of programming for speed. The company’s Operations Developer Sabina Smajlaj explained why in her recent talk at ScyllaDB‘s P99 Conf.
She considered multiple factors of async programming, the first being concurrency, but including other criteria such as memory usage and readability, writing async functions. Each method has its own best use case and it was interesting having these all laid out in one specific experience pros- and cons- talk.
How did each one stack up?
Why the Need for Async Code?
Async not only provides a better user experience, but since the entire application isn’t held up by every single event it leads to performance improvements, makes applications more robust and reliable, and aids in the scalability of applications.
Let’s start with a basic synchronous API.
Placing multiple orders to multiple markets looks like this.
Synchronous code isn’t optimized for high-frequency trading.
A thread runs the function in the background and multiple threads run at the same time. Once a thread starts, the code takes place in the background and the program is free to move forward with other tasks.
To launch a separate thread in Python, specify which function and pass in the arguments for the thread below. The image below is an example of the original place_orders API running on multiple threads with different parameters.
Concurrency will improve performance by splitting up tasks so they are only active when they have something to do, not when they’re waiting for server responses.
The time graph above includes time waiting to hear back from the markets but on the bottom half of the graph, in the Concurrent section, the white space shows there is no active work being done.
Through concurrency, it’s possible to schedule another task during the waiting period. There is a significant decrease in the amount of time the place orders method takes by taking advantage of time when the system isn’t doing active work.
But threads are not the optimal method for async programming for Hudson River Trading for several reasons. The most significant is that threads run in the background. This leads to two main issues: there isn’t a clear confirmation when an order is placed, the Order ID isn’t returned, and there is potential for the program to exit while the threads are still running. This makes threads a nonviable solution right off the bat.
Other challenges with threads include the error handling. In Python, if an exception is raised inside a thread the language just writes a message to the terminal and carries on. If there are multiple calls and threads, it’s challenging to identify where the exception came from in the stack trace. Threads are very memory heavy and some languages, such as Python, aren’t completely safe.
Threads are great for other use cases just not high-frequency trading. Wix used them to completely revamp its Node.js servers.
Callbacks are a type of API that is frequently used in network programming where rather than launching the thread yourself, the language takes care of running the request in the background. Callbacks provide a place to wait for multiple options to return, all in a single thread.
Though on the right track, callbacks fall short for high-frequency trading because of the lack of specificity. Let’s start with the lack of specificity with error handling.
Since error handling is a separate function within the callback, Smajlaj says, “it would be awkward to cancel some of the other orders.” The timeout is too general as well since there isn’t any way to place definitions or parameters on it. There’s also the issue that at the end of the place_orders function, the orders are still in an unknown state. How does anyone know if their order was placed?
Lastly, there’s the issue of code readability.
Smajlaj says, “This code doesn’t really read top to bottom since you have to keep jumping back up to look at the callback definitions.” The difficulties in readability cause difficulty in understanding the order in which events are taking place.
There is still a little too much vagueness with callbacks that render them nonviable for a high-frequency trading firm.
The third technique, async functions, are functions that pause or wait in the middle of execution while work takes place elsewhere. Async functions are defined like normal functions with the word “async” added before them.