Pre-release annoucement: Aeron ultra-low latency IPC/UDP multicast library

Aeron.jl

Hello all,
We’ve been working on wrapping the aeron c-library for use in Julia. Aeron is an open source library for reliable UDP unicast/multicast and shared memory IPC with a focus on very low latency and it originates from the HFT sector.

Our usecase for the library is IPC between components of an adaptive optics real time controller (astronomy), but I imagine there are many other use cases.

The wrapper provided by Aeron.jl allows one to publish and subscribe to messages with zero allocations, and no context switches (if using shared memory IPC). Currently you must provide and receive messages as byte vectors and any additional serialization or message packing must be done at the application layer.

Rationale

Several other IPC libraries exist for Julia, including the Base Distributed module and MPI.jl. Where aeron shines is ultra-low latency though I believe it’s throughput should be very good as well.

Example

This example assumes you have already started an Aeron media driver (for now you will have to compile aeronmd yourself from the upstream repo). You must start one media driver per user/computer.

Publisher

First, specify the stream. You can use an inter-process-communication channel or UDP / UDP multicast across a network.
The stream number is a unique integer to specify which stream of data to publish to / listen on.

conf = AeronConfig(
    channel="aeron:ipc",
    stream=1001,
)

Now start publishing to that stream. You have to convert your data into a vector of bytes (UInt8).
Interpretting those bytes is up to you. You could serialize / deserialize a message using any format
you want, or just send raw arrays.

Aeron.publisher(conf) do pub
    
    # For this test, send 10,000 bytes incrementing from 0:255 in a repeating cycle.
    message = zeros(UInt8, 10_000)
    
    for i in 1:100000

        message .= rem.(i, 255)


        # Sending a string:        
        # msg = "Hello world $i"
        # message = Vector{UInt8}(msg)

        # Status is a symbol to indicate if the publication was successful.
        status = Aeron.publication_offer(pub, message)

        @show status
        sleep(1)
    end
end

Subscriber

The subscriber looks similar, only you use a for loop to pull data frames out
of the subscription.

The main loop is completely allocation free.

conf = AeronConfig(
           channel="aeron:ipc",
           stream=1001,
       )

Aeron.subscribe(conf) do sub

    # Loop forever:
    # for frame in sub
    #     total = sum(frame.buffer)
    #     @info "Message received" total
    # end

    # Loop for 10 frames:
    for (i, frame) in enumerate(sub)
        total = sum(frame.buffer)
        @info "Message received" total
        if i > 10
            break
        end
    end

    # frame.buffer is a byte vector that is valid during that loop iteration only.

    return
end

Status

pre-beta and unregistered

The library ships with a compiled aeron client shared library but not yet an aeron media driver, which is necessary to run separately on each user/computer. This could be solved if someone is able to help with the relevant binary builder recipe.

Currently the Aeron_jll only supports linux, though it should be possible to support Mac OS and Windows if someone is able to help complete the binary builder recipes for those platforms.

We’d like to request feedback on the API. I’m trying to strike the right balance between low and high level and would love to hear what you think.

You can try it out here: GitHub - New-Earth-Lab/Aeron.jl: Julia wrappers for the Aeron IPC library.

8 Likes