Talos Goes Sonic
We purpose-built Sonic to reduce the time it takes to read and write data from the network with minimal latency. Compared to Go’s standard net package, Sonic achieves 5x to 50x lower latency on average to process incoming data from the network. Furthermore, Sonic gives us the control to ensure double-digit microsecond latency for our gateways. Today, we’re announcing that we’re open-sourcing the project.
Talos Goes Sonic
Introduction
We purpose-built Sonic to reduce the time it takes to read and write data from the network with minimal latency. Compared to Go’s standard net package, Sonic achieves 5x to 50x lower latency on average to process incoming data from the network. Furthermore, Sonic gives us the control to ensure double-digit microsecond latency for our gateways. Today, we’re announcing that we’re open-sourcing the project.
At Talos, we're building the best trading platform to help institutions trade crypto and digital assets. A benchmark for our platform's performance is minimizing slippage — the difference between the expected and actual execution price.
One way to reduce slippage is to reduce tick-to-trade latency, which is the time between receiving an order book update (“tick”) and sending a corresponding trading decision to the exchange (“trade”). While many factors contribute to tick-to-trade latency – geographic location, OS tuning, and application logic – an efficient networking library is key.
What is Sonic?
Sonic is a Go library for asynchronous network and I/O programming that allows developers to handle multiple file descriptors (such as sockets) asynchronously, in the same goroutine. It is fully event-driven and based on the Proactor model – a client specifies that it wants to read/write asynchronously and provides a callback to Sonic which is invoked when the read/write operation completes.
Why we created Sonic
The idea for Sonic arose from the need to ensure an upper bound to the time required to read+decode/write+encode data from trading venues. The first version of Talos's exchange connectivity was written with Go’s standard net package. While the Go concurrency model is perfect for the general use case, in the realm of trading, we want to have full control over our runtime. Sonic is superior to the Go net package on the following dimensions:
- Goroutines and concurrent data handling: In standard Go, concurrently reading/writing data must be done using goroutines. While they are lightweight in general, they do not provide optimal latency in tail cases, as each concurrent operation may run in parallel with another operation. There is no control over the underlying thread model. Furthermore, using goroutines means using synchronization primitives that limit us in terms of optimizations. With Sonic, we can have several connections read into the same buffer without using any synchronization primitives, like a mutex.
- Thread control and performance: While we can control the number of parallel threads through GOMAXPROCS, we needed something more granular. We use goroutines for non-latency-sensitive parts of the code that are mostly compute-bound, but we need Sonic for the latency-sensitive, mostly IO-bound parts. With Sonic, we can pin the gateway thread to an isolated core and have it busy-wait for data to minimize context switches, which is not easily achievable with Go’s standard net package.
- Full support for UDP Multicast: Some gateways deliver data through multiple UDP Multicast feeds and TCP connections in a custom binary format. Sonic fully supports UDP Multicast with source IP filtering, and several variants of ring buffers to efficiently handle multiple UDP multicast feeds in a single thread.
How Sonic improves latency
We ran an experiment to simulate receiving data from an exchange to assess Sonic's latency improvements compared to Go's standard networking libraries.
A Rust TCP server sends messages over several connections at a set rate: 10Hz, 100Hz, and 1000Hz. The rate applies to each connection in part. Each message is 1024 bytes. Just before sending a message, the Rust server takes a timestamp and places it in the message. The send is immediate and does not block. The server is asynchronous, single-threaded, and runs on an isolated core without kernel interrupts
We have two TCP clients connecting to the server: one using Sonic and the other using Go's standard networking libraries. Each client establishes several connections with the server and starts reading 1024-byte messages. After each read on a connection, we take a new timestamp and subtract from it the timestamp from the read message. We then plot the resulting difference in microseconds. (See Figure 1.). Both clients and the server run on the same host, on a tickless kernel. The Sonic client runs on an isolated core, while the Go/net client runs on 3 cores. None of the cores handle any kernel interrupts.
Figure 1: Message Latency of Sonic vs. Go/net at Different Messaging Rates
The maximum time taken for a Sonic and goroutine-based standard Go/net client to read a message over a different number of connections. A server writes a 1024-byte TCP message with time t1 at a set rate (10Hz, 100Hz, and 1000Hz). Each connection from the two clients reads the message from the socket and then takes the timestamp t2. We plot the maximum (t2 - t1) for each set of connections in microseconds.
The results show that using Sonic allows us to maintain consistent maximum latencies when receiving messages from an exchange. In contrast, the standard Go/net client shows variable latencies that peaked at over 2.5ms as the number of connections increased. Meanwhile, the Sonic client consistently delivered latencies below 100 microseconds regardless of the number of connections.
Sonic addresses key challenges in high-performance network communication, making it a superior solution for Talos and exchange gateway applications.
Find out more
To find out more about Sonic, visit: https://github.com/talostrading/sonic. We are currently looking for users and contributors as we work towards sonic v1.0.0!
Latest insights and research
Request a demo
Find out how Talos can simplify the way you interact with the digital asset markets.