Search code examples
c#javareal-timeassociative-arraylow-latency

C# to Java latency sensitive class conversion, would TreeMap replace SortedList in my case?


Im replicating a C# class in Java. (Im a Java newbie.)

My class needs to track int values associated with doubles. It then needs to create an Alerter(int) whenever a value crosses below or a above the doubles.

Alerter.LatencySensitiveAction() , needs to be called immediatly, it is latency sensitive and time critical code. The purpose of the DoubleMap Class is to call LatencySensitiveAction() as fast as possible.

DoubleMap.OnData() is the latency sensitive method of the class (below).

Does TreeMap make sense? I use SortedList in C#. Im looking for an associative collection that stores key/value pairs in sorted order with fast traversal.

I was told that this Java code

for (Map.Entry<Double,Alerter> entry : mAscend.entrySet() )

is not efficient because it creates a new object. What should I use instead?

So basically, i'm asking what collection to use that can associate a double with and int, is stored in sorted order, and what is the fastest way to traverse the collection in order.

I believe my C# code (below) does the job, need help with converting it to Java. If you think my C# code can be improved too.. please do tell.. ty.

Java Code:

public class DoubleMap {

    TreeMap<Double,Alerter> mAscend, mDecend, mHoldAscend, mHoldDecend;

    public DoubleMap()
    {
        mAscend = new TreeMap<Double, Alerter>();
        mDecend = new TreeMap<Double, Alerter>(new ReverseComparator());
    }

    public void Add(boolean rAscend, double value, int size)
    {
        TreeMap<Double,TradeOrder> list = rAscend ? mAscend : mDecend;

        Alerter to = list.get(value);
        if ( to != null )
        {
            Alerter.size += size;
        }
        else 
        {
            to = new Alerter (size);           
            list.put(value, to);
        }
    }

    public void Remove(boolean rAscend, double value, int size)
    {
        TreeMap<Double,TradeOrder> list = rAscend ? mAscend : mDecend;

        Alerter to = list.get(value);
        if ( to != null )
        {
            long nsize = to.size - size;
            if ( nsize <= 0 )
                list.remove(value);
            else
                to.size = nsize;
        }
    }

    public void Ondata(double rValue)
    {
        for (Map.Entry<Double,Alerter> entry : mAscend.entrySet() )
        {
            if ( entry.getKey() > rValue )
                break;

            entry.getValue().LatencySensitiveAction();

            if ( mHoldAscend == null )
                mHoldAscend = new TreeMap<Double,Alerter>(mHoldAscend);
            mAscend.remove(entry.getKey());
        }

        for (Map.Entry<Double,TradeOrder> entry : mDecend.entrySet() )
        {
            if ( entry.getKey() < rValue )
                break;

            entry.getValue().LatencySensitiveAction();

            if ( mHoldDecend == null )
                mHoldDecend = new TreeMap<Double,TradeOrder>(mHoldDecend);
            mHoldDecend.remove(entry.getKey());
        }

        if ( mHoldAscend != null )
        {
            mAscend = mHoldAscend;
            mHoldAscend = null;
        }

        if ( mHoldDecend != null )
        {
            mDecend = mHoldDecend;
            mHoldDecend = null;
        }

    }
}

C# Code:

public class DoubleMap
{
    private SortedList<double, Alerter> mAscend, mDecend, mHoldAscend, mHoldDecend;

    public DoubleMap()
    {
        mAscend = new SortedList<double, Alerter>();
        mDecend = new SortedList<double, Alerter>(new DescendingComparer<double>());
    }

    public void Add(bool rAscend, double rValue, long rSize)
    {
        var list = rAscend ? mAscend : mDecend;
        Alerter to;
        if (list.TryGetValue(rValue, out to))
        {
            to.Size += rSize;
        }
        else
        {
            to = new Alerter(rSize);
            list.Add(rValue, to);
        }
    }

    public void Remove(bool rAscend, double rValue, long rSize)
    {
        var list = rAscend ? mAscend : mDecend;
        Alerter to;
        if (list.TryGetValue(rValue, out to))
        {
            long nqty = to.Size - rSize;
            if (nqty <= 0)
            {
                list.Remove(rValue);
            }
            else
                to.Size = nqty;
        }
    }

    public void OnData(double rValue)
    {
        foreach (var pair in mAscend)
        {
            if (pair.Key > rValue)
                break;

            pair.Value.LatencySensitiveAction();

            if (mHoldAscend == null)
                mHoldAscend = new SortedList<double, Alerter>(mAscend);
            mHoldAscend.Remove(pair.Key);
        }

        foreach (var pair in mDecend)
        {
            if (pair.Key < rValue)
                break;

            pair.Value.LatencySensitiveAction();

            if (mHoldDecend == null)
                mHoldDecend = new SortedList<double, Alerter>(mDecend, new DescendingComparer<double>());
            mHoldDecend.Remove(pair.Key);
        }

        if (mHoldAscend != null)
        {
            mAscend = mHoldAscend;
            mHoldAscend = null;
        }

        if (mHoldDecend != null)
        {
            mDecend = mHoldDecend;
            mHoldDecend = null;
        }
    }
}

class DescendingComparer<T> : IComparer<T> where T : IComparable<T>
{
    public int Compare(T x, T y)
    {
        return y.CompareTo(x);
    }
}

Solution

  • If latency is so important for you, I'd recommend implementing a custom class to handle your order book (this is an order book, right? :-) )

    I'd suggest something like the following:

    • An array of doubles representing the prices (p.s. why are you using doubles? shouldn't this be either BigDecimal or a fixed point integer to avoid floating point inaccuracy?)
    • A corresponding array of longs for the order sizes
    • A corresponding array of alerters
    • Sort all arrays according to price
    • You can then use a binary search directly on the price array to seek out prices in any given range

    This approach will give you very low latency.

    Downside is that it's O(n) to add/remove orders. But it's such a cheap O(n) with just three arraycopys, that unless your order book is very large the overhead will be so low that you won't notice.

    You may also be interested in Javolution which contained a set of very low latency libraries for Java, I think some of which are specifically designed for trading applications.